diff --git a/.hgtags b/.hgtags index dd49fb5ce19..7f61e8eb2e7 100644 --- a/.hgtags +++ b/.hgtags @@ -489,3 +489,4 @@ e1e60f75cd39312a7f59d2a4f91d624e5aecc95e jdk-11+11 a11c1cb542bbd1671d25b85efe7d09b983c48525 jdk-11+15 02934b0d661b82b7fe1052a04998d2091352e08d jdk-11+16 64e4b1686141e57a681936a8283983341484676e jdk-11+17 +e1b3def126240d5433902f3cb0e91a4c27f6db50 jdk-11+18 diff --git a/doc/building.html b/doc/building.html index 3c982e9d4ca..9c1dc0453b0 100644 --- a/doc/building.html +++ b/doc/building.html @@ -158,7 +158,7 @@

Operating System Requirements

The mainline OpenJDK project supports Linux, Solaris, macOS, AIX and Windows. Support for other operating system, e.g. BSD, exists in separate "port" projects.

In general, OpenJDK can be built on a wide range of versions of these operating systems, but the further you deviate from what is tested on a daily basis, the more likely you are to run into problems.

-

This table lists the OS versions used by Oracle when building JDK 9. Such information is always subject to change, but this table is up to date at the time of writing.

+

This table lists the OS versions used by Oracle when building OpenJDK. Such information is always subject to change, but this table is up to date at the time of writing.

@@ -190,7 +190,7 @@

Windows

Windows XP is not a supported platform, but all newer Windows should be able to build OpenJDK.

On Windows, it is important that you pay attention to the instructions in the Special Considerations.

-

Windows is the only non-POSIX OS supported by OpenJDK, and as such, requires some extra care. A POSIX support layer is required to build on Windows. For OpenJDK 9, the only supported such layer is Cygwin. (Msys is no longer supported due to a too old bash; msys2 and the new Windows Subsystem for Linux (WSL) would likely be possible to support in a future version but that would require a community effort to implement.)

+

Windows is the only non-POSIX OS supported by OpenJDK, and as such, requires some extra care. A POSIX support layer is required to build on Windows. Currently, the only supported such layer is Cygwin. (Msys is no longer supported due to a too old bash; msys2 and the new Windows Subsystem for Linux (WSL) would likely be possible to support in a future version but that would require a community effort to implement.)

Internally in the build system, all paths are represented as Unix-style paths, e.g. /cygdrive/c/hg/jdk9/Makefile rather than C:\hg\jdk9\Makefile. This rule also applies to input to the build system, e.g. in arguments to configure. So, use --with-msvcr-dll=/cygdrive/c/msvcr100.dll rather than --with-msvcr-dll=c:\msvcr100.dll. For details on this conversion, see the section on Fixpath.

Cygwin

A functioning Cygwin environment is thus required for building OpenJDK on Windows. If you have a 64-bit OS, we strongly recommend using the 64-bit version of Cygwin.

@@ -265,7 +265,7 @@ - + @@ -282,8 +282,8 @@
Linuxgcc 4.9.2gcc 7.3.0
macOS

gcc

-

The minimum accepted version of gcc is 4.7. Older versions will generate a warning by configure and are unlikely to work.

-

OpenJDK 9 includes patches that should allow gcc 6 to compile, but this should be considered experimental.

+

The minimum accepted version of gcc is 4.8. Older versions will generate a warning by configure and are unlikely to work.

+

OpenJDK is currently known to be able to compile with at least version 7.4 of gcc.

In general, any version between these two should be usable.

clang

The minimum accepted version of clang is 3.2. Older versions will not be accepted by configure.

@@ -788,7 +788,7 @@ sudo mv /tmp/configure /usr/local/bin

The default behavior for make is to create consistent and correct output, at the expense of build speed, if necessary.

If you are prepared to take some risk of an incorrect build, and know enough of the system to understand how things build and interact, you can speed up the build process considerably by instructing make to only build a portion of the product.

Building Individual Modules

-

The safe way to use fine-grained make targets is to use the module specific make targets. All source code in JDK 9 is organized so it belongs to a module, e.g. java.base or jdk.jdwp.agent. You can build only a specific module, by giving it as make target: make jdk.jdwp.agent. If the specified module depends on other modules (e.g. java.base), those modules will be built first.

+

The safe way to use fine-grained make targets is to use the module specific make targets. All source code in OpenJDK is organized so it belongs to a module, e.g. java.base or jdk.jdwp.agent. You can build only a specific module, by giving it as make target: make jdk.jdwp.agent. If the specified module depends on other modules (e.g. java.base), those modules will be built first.

You can also specify a set of modules, just as you can always specify a set of make targets: make jdk.crypto.cryptoki jdk.crypto.ec jdk.crypto.mscapi jdk.crypto.ucrypto

Building Individual Module Phases

The build process for each module is divided into separate phases. Not all modules need all phases. Which are needed depends on what kind of source code and other artifact the module consists of. The phases are:

diff --git a/doc/building.md b/doc/building.md index 8b9b81391c7..a93d5af8e2b 100644 --- a/doc/building.md +++ b/doc/building.md @@ -135,7 +135,7 @@ In general, OpenJDK can be built on a wide range of versions of these operating systems, but the further you deviate from what is tested on a daily basis, the more likely you are to run into problems. -This table lists the OS versions used by Oracle when building JDK 9. Such +This table lists the OS versions used by Oracle when building OpenJDK. Such information is always subject to change, but this table is up to date at the time of writing. @@ -164,8 +164,8 @@ On Windows, it is important that you pay attention to the instructions in the [Special Considerations](#special-considerations). Windows is the only non-POSIX OS supported by OpenJDK, and as such, requires -some extra care. A POSIX support layer is required to build on Windows. For -OpenJDK 9, the only supported such layer is Cygwin. (Msys is no longer +some extra care. A POSIX support layer is required to build on Windows. +Currently, the only supported such layer is Cygwin. (Msys is no longer supported due to a too old bash; msys2 and the new Windows Subsystem for Linux (WSL) would likely be possible to support in a future version but that would require a community effort to implement.) @@ -291,18 +291,18 @@ issues. Operating system Toolchain version ------------------ ------------------------------------------------------- - Linux gcc 4.9.2 + Linux gcc 7.3.0 macOS Apple Xcode 6.3 (using clang 6.1.0) Solaris Oracle Solaris Studio 12.4 (with compiler version 5.13) Windows Microsoft Visual Studio 2013 update 4 ### gcc -The minimum accepted version of gcc is 4.7. Older versions will generate a warning +The minimum accepted version of gcc is 4.8. Older versions will generate a warning by `configure` and are unlikely to work. -OpenJDK 9 includes patches that should allow gcc 6 to compile, but this should -be considered experimental. +OpenJDK is currently known to be able to compile with at least version 7.4 of +gcc. In general, any version between these two should be usable. @@ -1460,10 +1460,11 @@ product. #### Building Individual Modules The safe way to use fine-grained make targets is to use the module specific -make targets. All source code in JDK 9 is organized so it belongs to a module, -e.g. `java.base` or `jdk.jdwp.agent`. You can build only a specific module, by -giving it as make target: `make jdk.jdwp.agent`. If the specified module -depends on other modules (e.g. `java.base`), those modules will be built first. +make targets. All source code in OpenJDK is organized so it belongs to a +module, e.g. `java.base` or `jdk.jdwp.agent`. You can build only a specific +module, by giving it as make target: `make jdk.jdwp.agent`. If the specified +module depends on other modules (e.g. `java.base`), those modules will be built +first. You can also specify a set of modules, just as you can always specify a set of make targets: `make jdk.crypto.cryptoki jdk.crypto.ec jdk.crypto.mscapi diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk index 058f9a58eb8..15f9eaf439e 100644 --- a/make/CompileJavaModules.gmk +++ b/make/CompileJavaModules.gmk @@ -519,6 +519,7 @@ jdk.localedata_COPY += _dict _th # Exclude BreakIterator classes that are just used in compile process to generate # data files and shouldn't go in the product jdk.localedata_EXCLUDE_FILES += sun/text/resources/ext/BreakIteratorRules_th.java +jdk.localedata_KEEP_ALL_TRANSLATIONS := true ################################################################################ # There is an issue in sjavac that triggers a warning in jdk.jfr that isn't diff --git a/make/Init.gmk b/make/Init.gmk index fa1c5e6fdaf..8cbc81b08e1 100644 --- a/make/Init.gmk +++ b/make/Init.gmk @@ -298,7 +298,6 @@ else # HAS_SPEC=true main: $(INIT_TARGETS) ifneq ($(SEQUENTIAL_TARGETS)$(PARALLEL_TARGETS), ) $(call RotateLogFiles) - $(call PrepareFailureLogs) $(PRINTF) "Building $(TARGET_DESCRIPTION)\n" $(BUILD_LOG_PIPE) ifneq ($(SEQUENTIAL_TARGETS), ) # Don't touch build output dir since we might be cleaning. That @@ -308,6 +307,7 @@ else # HAS_SPEC=true $(SEQUENTIAL_TARGETS) ) endif ifneq ($(PARALLEL_TARGETS), ) + $(call PrepareFailureLogs) $(call StartGlobalTimer) $(call PrepareSmartJavac) # JOBS will only be empty for a bootcycle-images recursive call diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk index e885e0fb1a3..8f2d2b92707 100644 --- a/make/InitSupport.gmk +++ b/make/InitSupport.gmk @@ -456,6 +456,9 @@ else # $(HAS_SPEC)=true ) endef + # Failure logs are only supported for "parallel" main targets, not the + # (trivial) sequential make targets (such as clean and reconfigure), + # since the failure-logs directory creation will conflict with clean. define PrepareFailureLogs $(RM) -r $(MAKESUPPORT_OUTPUTDIR)/failure-logs 2> /dev/null && \ $(MKDIR) -p $(MAKESUPPORT_OUTPUTDIR)/failure-logs diff --git a/make/ZipSource.gmk b/make/ZipSource.gmk index 9b03ed98cd5..f2aefeb846f 100644 --- a/make/ZipSource.gmk +++ b/make/ZipSource.gmk @@ -68,6 +68,17 @@ TARGETS += $(SRC_ZIP_SRCS) # Only evaluate the creation of src.zip in a sub make call when the symlinked # src directory structure has been generated. ifeq ($(SRC_GENERATED), true) + + # Rewrite the EXCLUDE_TRANSLATIONS locales as exclude patters for java files + TRANSLATIONS_PATTERN := $(addprefix %_, $(addsuffix .java, $(EXCLUDE_TRANSLATIONS))) + + # Add excludes for translations for all modules except jdk.localedata + $(foreach s, $(SRC_ZIP_SRCS), \ + $(if $(filter $(notdir $s), jdk.localedata), , \ + $(eval BUILD_SRC_ZIP_EXCLUDE_PATTERNS_$(dir $s) := $$(TRANSLATIONS_PATTERN)) \ + ) \ + ) + $(eval $(call SetupZipArchive, BUILD_SRC_ZIP, \ SRC := $(dir $(SRC_ZIP_SRCS)), \ INCLUDES := $(SRC_ZIP_INCLUDES), \ diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 936af504e86..9aaab365ec4 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -31,7 +31,7 @@ SRC# AC_PREREQ([2.69]) -AC_INIT(OpenJDK, jdk9, build-dev@openjdk.java.net,,http://openjdk.java.net) +AC_INIT(OpenJDK, openjdk, build-dev@openjdk.java.net,,http://openjdk.java.net) AC_CONFIG_AUX_DIR([$TOPDIR/make/autoconf/build-aux]) m4_include([build-aux/pkg.m4]) @@ -232,6 +232,7 @@ HOTSPOT_SETUP_JVM_FEATURES JDKOPT_DETECT_INTREE_EC JDKOPT_ENABLE_DISABLE_FAILURE_HANDLER JDKOPT_ENABLE_DISABLE_GENERATE_CLASSLIST +JDKOPT_EXCLUDE_TRANSLATIONS ############################################################################### # diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index e29a4db2d7a..38cb0fbdff0 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -573,22 +573,24 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], # '-qpic' defaults to 'qpic=small'. This means that the compiler generates only # one instruction for accessing the TOC. If the TOC grows larger than 64K, the linker # will have to patch this single instruction with a call to some out-of-order code which - # does the load from the TOC. This is of course slow. But in that case we also would have + # does the load from the TOC. This is of course slower, and we also would have # to use '-bbigtoc' for linking anyway so we could also change the PICFLAG to 'qpic=large'. # With 'qpic=large' the compiler will by default generate a two-instruction sequence which # can be patched directly by the linker and does not require a jump to out-of-order code. - # Another alternative instead of using 'qpic=large -bbigtoc' may be to use '-qminimaltoc' - # instead. This creates a distinct TOC for every compilation unit (and thus requires two - # loads for accessing a global variable). But there are rumors that this may be seen as a - # 'performance feature' because of improved code locality of the symbols used in a - # compilation unit. - PICFLAG="-qpic" + # + # Since large TOC causes perf. overhead, only pay it where we must. Currently this is + # for all libjvm variants (both gtest and normal) but no other binaries. So, build + # libjvm with -qpic=large and link with -bbigtoc. + JVM_PICFLAG="-qpic=large" + JDK_PICFLAG="-qpic" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then PICFLAG="" fi - JVM_PICFLAG="$PICFLAG" - JDK_PICFLAG="$PICFLAG" + if test "x$TOOLCHAIN_TYPE" != xxlc; then + JVM_PICFLAG="$PICFLAG" + JDK_PICFLAG="$PICFLAG" + fi if test "x$OPENJDK_TARGET_OS" = xmacosx; then # Linking is different on MacOSX diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index c83f6cbde9f..6075fdccabb 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -94,7 +94,8 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], elif test "x$TOOLCHAIN_TYPE" = xxlc; then BASIC_LDFLAGS="-b64 -brtl -bnolibpath -bexpall -bernotok -btextpsize:64K \ -bdatapsize:64K -bstackpsize:64K" - BASIC_LDFLAGS_JVM_ONLY="-Wl,-lC_r" + # libjvm.so has gotten too large for normal TOC size; compile with qpic=large and link with bigtoc + BASIC_LDFLAGS_JVM_ONLY="-Wl,-lC_r -bbigtoc" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then BASIC_LDFLAGS="-nologo -opt:ref" diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 21d77167dd3..9e13eb73f3c 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -582,3 +582,25 @@ AC_DEFUN_ONCE([JDKOPT_ENABLE_DISABLE_GENERATE_CLASSLIST], AC_SUBST(ENABLE_GENERATE_CLASSLIST) ]) + +################################################################################ +# +# Optionally filter resource translations +# +AC_DEFUN([JDKOPT_EXCLUDE_TRANSLATIONS], +[ + AC_ARG_WITH([exclude-translations], [AS_HELP_STRING([--with-exclude-translations], + [a comma separated list of locales to exclude translations for. Default is + to include all translations present in the source.])]) + + EXCLUDE_TRANSLATIONS="" + AC_MSG_CHECKING([if any translations should be excluded]) + if test "x$with_exclude_translations" != "x"; then + EXCLUDE_TRANSLATIONS="${with_exclude_translations//,/ }" + AC_MSG_RESULT([yes: $EXCLUDE_TRANSLATIONS]) + else + AC_MSG_RESULT([no]) + fi + + AC_SUBST(EXCLUDE_TRANSLATIONS) +]) diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 8de0abeee81..ec45949e9cb 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -79,7 +79,7 @@ OPENJDK_TARGET_CPU_ISADIR:=@OPENJDK_TARGET_CPU_ISADIR@ OPENJDK_TARGET_CPU_LEGACY:=@OPENJDK_TARGET_CPU_LEGACY@ OPENJDK_TARGET_CPU_LEGACY_LIB:=@OPENJDK_TARGET_CPU_LEGACY_LIB@ OPENJDK_TARGET_CPU_OSARCH:=@OPENJDK_TARGET_CPU_OSARCH@ -OPENJDK_TARGET_OS_INCLUDE_SUBIDR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ +OPENJDK_TARGET_OS_INCLUDE_SUBDIR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ HOTSPOT_TARGET_OS := @HOTSPOT_TARGET_OS@ HOTSPOT_TARGET_OS_TYPE := @HOTSPOT_TARGET_OS_TYPE@ @@ -102,7 +102,7 @@ OPENJDK_BUILD_CPU_ARCH:=@OPENJDK_BUILD_CPU_ARCH@ OPENJDK_BUILD_CPU_BITS:=@OPENJDK_BUILD_CPU_BITS@ OPENJDK_BUILD_CPU_ENDIAN:=@OPENJDK_BUILD_CPU_ENDIAN@ -OPENJDK_BUILD_OS_INCLUDE_SUBIDR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ +OPENJDK_BUILD_OS_INCLUDE_SUBDIR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ # Target platform value in ModuleTarget class file attribute. OPENJDK_MODULE_TARGET_PLATFORM:=@OPENJDK_MODULE_TARGET_PLATFORM@ @@ -303,6 +303,8 @@ BUILD_FAILURE_HANDLER := @BUILD_FAILURE_HANDLER@ ENABLE_GENERATE_CLASSLIST := @ENABLE_GENERATE_CLASSLIST@ +EXCLUDE_TRANSLATIONS := @EXCLUDE_TRANSLATIONS@ + # The boot jdk to use. This is overridden in bootcycle-spec.gmk. Make sure to keep # it in sync. BOOT_JDK:=@BOOT_JDK@ diff --git a/make/common/JavaCompilation.gmk b/make/common/JavaCompilation.gmk index 4fbd0d9241e..a1cdd9264d7 100644 --- a/make/common/JavaCompilation.gmk +++ b/make/common/JavaCompilation.gmk @@ -180,6 +180,7 @@ endef # CREATE_API_DIGEST:=Set to true to use a javac plugin to generate a public API # hash which can be used for down stream dependencies to only rebuild # when the API changes. Implicitly used in sjavac. +# KEEP_ALL_TRANSLATIONS:=Set to true to skip translation filtering SetupJavaCompilation = $(NamedParamsMacroTemplate) define SetupJavaCompilationBody @@ -266,6 +267,11 @@ define SetupJavaCompilationBody $$(eval $1_$$(relative_src) := 1) $$(s)))) endif + # Filter out any excluded translations + ifneq ($$($1_KEEP_ALL_TRANSLATIONS), true) + $1_SRCS := $$(call FilterExcludedTranslations, $$($1_SRCS), .java) + endif + ifeq ($$(strip $$($1_SRCS)), ) ifneq ($$($1_FAIL_NO_SRC), false) $$(error No source files found for $1) @@ -290,6 +296,10 @@ define SetupJavaCompilationBody ifneq (,$$($1_EXCLUDE_PATTERN)) $1_ALL_COPIES := $$(filter-out $$($1_EXCLUDE_PATTERN),$$($1_ALL_COPIES)) endif + # Filter out any excluded translations + ifneq ($$($1_KEEP_ALL_TRANSLATIONS), true) + $1_ALL_COPIES := $$(call FilterExcludedTranslations, $$($1_ALL_COPIES), .properties) + endif ifneq (,$$($1_ALL_COPIES)) # Yep, there are files to be copied! $1_ALL_COPY_TARGETS:= @@ -310,6 +320,10 @@ define SetupJavaCompilationBody ifneq (,$$($1_EXCLUDE_PATTERN)) $1_ALL_CLEANS := $$(filter-out $$($1_EXCLUDE_PATTERN),$$($1_ALL_CLEANS)) endif + # Filter out any excluded translations + ifneq ($$($1_KEEP_ALL_TRANSLATIONS), true) + $1_ALL_CLEANS := $$(call FilterExcludedTranslations, $$($1_ALL_CLEANS), .properties) + endif ifneq (,$$($1_ALL_CLEANS)) # Yep, there are files to be copied and cleaned! $1_ALL_COPY_CLEAN_TARGETS:= diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index b3a59e14aa1..d397fe6f9c8 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -1074,6 +1074,22 @@ ColonList = \ $(subst ::,:,$(subst $(SPACE),:,$(strip $1))) \ ) +################################################################################ +# Given a list of files, filters out locale specific files for translations +# that should be excluded from this build. +# $1 - The list of files to filter +# $2 - The suffix of the files that should be considered (.java or .properties) +FilterExcludedTranslations = \ + $(strip $(if $(EXCLUDE_TRANSLATIONS), \ + $(filter-out \ + $(foreach suffix, $2, \ + $(addprefix %_, $(addsuffix $(suffix), $(EXCLUDE_TRANSLATIONS))) \ + ), \ + $1 \ + ), \ + $1 \ + )) + ################################################################################ # Hook to include the corresponding custom file, if present. diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index f4646f16efd..6a0be34df67 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -77,6 +77,7 @@ BOOT_MODULES += \ # should carefully be considered if it should be upgradeable or not. UPGRADEABLE_MODULES += \ java.compiler \ + jdk.aot \ jdk.internal.vm.compiler \ jdk.internal.vm.compiler.management \ # diff --git a/make/common/ZipArchive.gmk b/make/common/ZipArchive.gmk index eae37b5ffde..4ced471d9aa 100644 --- a/make/common/ZipArchive.gmk +++ b/make/common/ZipArchive.gmk @@ -42,6 +42,10 @@ endif # INCLUDE_FILES # EXCLUDES # EXCLUDE_FILES +# EXCLUDE_PATTERNS - Patterns with at most one % wildcard matching filenames +# and not directories. +# EXCLUDE_PATTERNS_$dir - Exclude patterns just like above but specific to one +# src dir # SUFFIXES # EXTRA_DEPS # ZIP_OPTIONS extra options to pass to zip @@ -88,11 +92,26 @@ define SetupZipArchiveBody $1_ALL_SRCS := $$(filter-out $$($1_SRC_EXCLUDES),$$($1_ALL_SRCS)) endif ifneq ($$($1_EXCLUDE_FILES),) - # Cannot precompute ZIP_EXCLUDE_FILES as it is dependent on which src root is being - # zipped at the moment. $1_SRC_EXCLUDE_FILES := $$(addprefix %, $$($1_EXCLUDE_FILES)) $$($1_EXCLUDE_FILES) $1_ALL_SRCS := $$(filter-out $$($1_SRC_EXCLUDE_FILES), $$($1_ALL_SRCS)) + $$(foreach s, $$($1_SRC), \ + $$(eval $1_ZIP_EXCLUDES_$$s += \ + $$(addprefix -x$$(SPACE), $$(patsubst $$s/%,%, $$($1_EXCLUDE_FILES))) \ + ) \ + ) endif + ifneq ($$($1_EXCLUDE_PATTERNS), ) + $1_ALL_SRCS := $$(filter-out $$($1_EXCLUDE_PATTERNS), $$($1_ALL_SRCS)) + $1_ZIP_EXCLUDES += $$(addprefix -x$(SPACE), $$(subst %,\*,$$($1_EXCLUDE_PATTERNS))) + endif + # Rewrite src dir specific exclude patterns to zip excludes + $$(foreach s, $$($1_SRC), \ + $$(if $$($1_EXCLUDE_PATTERNS_$$s), \ + $$(eval $1_ZIP_EXCLUDES_$$s += \ + $$(addprefix -x$$(SPACE), $$(subst %,\*,$$($1_EXCLUDE_PATTERNS_$$s))) \ + ) \ + ) \ + ) # Use a slightly shorter name for logging, but with enough path to identify this zip. $1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_ZIP)) @@ -107,9 +126,9 @@ define SetupZipArchiveBody $$($1_ZIP) : $$($1_ALL_SRCS) $$($1_EXTRA_DEPS) $(MKDIR) -p $$(@D) $(ECHO) Updating $$($1_NAME) - $$(foreach i,$$($1_SRC),(cd $$i && $(ZIPEXE) -qru $$($1_ZIP_OPTIONS) $$@ . $$($1_ZIP_INCLUDES) \ - $$($1_ZIP_EXCLUDES) -x \*_the.\* \ - $$(addprefix -x$(SPACE), $$(patsubst $$i/%,%, $$($1_EXCLUDE_FILES))) \ + $$(foreach s,$$($1_SRC),(cd $$s && $(ZIPEXE) -qru $$($1_ZIP_OPTIONS) $$@ . \ + $$($1_ZIP_INCLUDES) $$($1_ZIP_EXCLUDES) -x \*_the.\* \ + $$($1_ZIP_EXCLUDES_$$s) \ || test "$$$$?" = "12" )$$(NEWLINE)) true $(TOUCH) $$@ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 9324ccb6722..b858ea992b7 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -242,7 +242,8 @@ var getJibProfilesCommon = function (input, data) { dependencies: ["boot_jdk", "gnumake", "jtreg", "jib"], default_make_targets: ["product-bundles", "test-bundles"], configure_args: concat(["--enable-jtreg-failure-handler"], - versionArgs(input, common)) + "--with-exclude-translations=de,es,fr,it,ko,pt_BR,sv,ca,tr,cs,sk,ja_JP_A,ja_JP_HA,ja_JP_HI,ja_JP_I", + versionArgs(input, common)) }; // Extra settings for debug profiles common.debug_suffix = "-debug"; @@ -438,6 +439,7 @@ var getJibProfilesProfiles = function (input, common, data) { dependencies: ["devkit", "autoconf", "build_devkit", "cups"], configure_args: [ "--openjdk-target=aarch64-linux-gnu", "--with-freetype=bundled", + "--disable-warnings-as-errors", "--with-cpu-port=aarch64", ], }, @@ -568,6 +570,29 @@ var getJibProfilesProfiles = function (input, common, data) { profiles[debugName] = concatObjects(profiles[name], common.debug_profile_base); }); + // Bootcycle profiles runs the build with itself as the boot jdk. This can + // be done in two ways. Either using the builtin bootcycle target in the + // build system. Or by supplying the main jdk build as bootjdk to configure. + [ "linux-x64", "macosx-x64", "solaris-sparcv9", "windows-x64"] + .forEach(function (name) { + var bootcycleName = name + "-bootcycle"; + var bootcyclePrebuiltName = name + "-bootcycle-prebuilt"; + // The base bootcycle profile just changes the default target + // compared to the base profile + profiles[bootcycleName] = clone(profiles[name]); + profiles[bootcycleName].default_make_targets = [ "bootcycle-images" ]; + // The prebuilt bootcycle variant modifies the boot jdk argument + var bootcyclePrebuiltBase = { + dependencies: [ name + ".jdk" ], + configure_args: "--with-boot-jdk=" + input.get(name + ".jdk", "home_path"), + } + profiles[bootcyclePrebuiltName] = concatObjects(profiles[name], + bootcyclePrebuiltBase); + var bootJdkIndex = profiles[bootcyclePrebuiltName].dependencies.indexOf("boot_jdk"); + delete profiles[bootcyclePrebuiltName].dependencies[bootJdkIndex]; + profiles[bootcyclePrebuiltName].default_make_targets = [ "product-images" ]; + }); + // // Define artifacts for profiles // diff --git a/make/gensrc/Gensrc-jdk.localedata.gmk b/make/gensrc/Gensrc-jdk.localedata.gmk index 137ce73e0f6..20795487480 100644 --- a/make/gensrc/Gensrc-jdk.localedata.gmk +++ b/make/gensrc/Gensrc-jdk.localedata.gmk @@ -38,6 +38,7 @@ include GensrcProperties.gmk $(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ SRC_DIRS := $(TOPDIR)/src/jdk.localedata/share/classes/sun/util/resources, \ CLASS := sun.util.resources.LocaleNamesBundle, \ + KEEP_ALL_TRANSLATIONS := true, \ )) # Skip generating zh_HK from zh_TW for this module. diff --git a/make/gensrc/GensrcCLDR.gmk b/make/gensrc/GensrcCLDR.gmk index f75b04a826e..870cabe5501 100644 --- a/make/gensrc/GensrcCLDR.gmk +++ b/make/gensrc/GensrcCLDR.gmk @@ -23,7 +23,7 @@ # questions. # -CLDRVERSION := 29.0.0 +CLDRVERSION := 33 CLDRSRCDIR := $(TOPDIR)/src/jdk.localedata/share/classes/sun/util/cldr/resources/common GENSRC_BASEDIR := $(SUPPORT_OUTPUTDIR)/gensrc/java.base diff --git a/make/gensrc/GensrcCommonLangtools.gmk b/make/gensrc/GensrcCommonLangtools.gmk index 97b5a5dd24f..682808c37ab 100644 --- a/make/gensrc/GensrcCommonLangtools.gmk +++ b/make/gensrc/GensrcCommonLangtools.gmk @@ -66,6 +66,9 @@ define SetupCompileProperties PROPSOURCES := $2 \ $$(shell $(FIND) $(TOPDIR)/src/$(MODULE)/share/classes -name "*.properties") + # Filter out any excluded translations + PROPSOURCES := $$(call FilterExcludedTranslations, $$(PROPSOURCES), .properties) + # Convert .../src//share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties # to .../langtools/gensrc//com/sun/tools/javac/resources/javac_zh_CN.java # Strip away prefix and suffix, leaving for example only: @@ -105,6 +108,7 @@ endef define SetupParseProperties # property files to process PARSEPROPSOURCES := $$(addprefix $(TOPDIR)/src/$(MODULE)/share/classes/, $2) + PARSEPROPSOURCES := $$(call FilterExcludedTranslations, $$(PARSEPROPSOURCES), .properties) PARSEPROPALLDIRS := $$(patsubst $(TOPDIR)/src/$(MODULE)/share/classes/%, \ $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/%, \ diff --git a/make/gensrc/GensrcProperties.gmk b/make/gensrc/GensrcProperties.gmk index b0381e9159c..c8d17a35539 100644 --- a/make/gensrc/GensrcProperties.gmk +++ b/make/gensrc/GensrcProperties.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -59,6 +59,7 @@ endef # EXCLUDE Exclude files matching this pattern. # CLASS The super class for the generated classes. # MODULE_PATH_ROOT Module path root, defaults to $(TOPDIR)/src. +# KEEP_ALL_TRANSLATIONS Set to true to skip filtering of excluded translations. SetupCompileProperties = $(NamedParamsMacroTemplate) define SetupCompilePropertiesBody # Set default value unless overridden @@ -73,10 +74,13 @@ define SetupCompilePropertiesBody $1_SRC_FILES := $$(filter-out $$($1_EXCLUDE), $$($1_SRC_FILES)) endif + # Filter out any excluded translations + ifneq ($$($1_KEEP_ALL_TRANSLATIONS), true) + $1_SRC_FILES := $$(call FilterExcludedTranslations, $$($1_SRC_FILES), .properties) + endif + # Convert .../src//share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties # to .../support/gensrc//com/sun/tools/javac/resources/javac_zh_CN.java - # Strip away prefix and suffix, leaving for example only: - # "/share/classes/com/sun/tools/javac/resources/javac_zh_CN" $1_JAVAS := $$(patsubst $$($1_MODULE_PATH_ROOT)/%, \ $(SUPPORT_OUTPUTDIR)/gensrc/%, \ $$(patsubst %.properties, %.java, \ @@ -99,7 +103,7 @@ define SetupCompilePropertiesBody $1_TARGET := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/_the.$1.marker $1_CMDLINE_FILE := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/_the.$1.cmdline -# Now setup the rule for the generation of the resource bundles. + # Now setup the rule for the generation of the resource bundles. $$($1_TARGET): $$($1_SRC_FILES) $$($1_JAVAS) $(BUILD_TOOLS_JDK) $(MKDIR) -p $$(@D) $$($1_DIRS) $(ECHO) Compiling $$(words $$($1_SRC_FILES)) properties into resource bundles for $(MODULE) diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index 3f2f4b3c16f..e288482b115 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -55,13 +55,6 @@ endif # Disabling undef, switch, format-nonliteral and tautological-undefined-compare # warnings for clang because of test source. -# Note: On AIX, the gtest test classes linked into the libjvm.so push the TOC -# size beyond 64k, so we need to link with bigtoc. However, this means that -# -qpic=large would be advisable to lessen the performance effect of bigtoc. -# But we want to avoid imposing -qpic=large onto the regular libjvm.so, which -# has no problem with its TOC, so do this only for object files which are -# exclusive to the gtest libjvm.so. - $(eval $(call SetupNativeCompilation, BUILD_GTEST_LIBJVM, \ NAME := jvm, \ TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ @@ -80,7 +73,6 @@ $(eval $(call SetupNativeCompilation, BUILD_GTEST_LIBJVM, \ CFLAGS_windows := -EHsc, \ CFLAGS_solaris := -DGTEST_HAS_EXCEPTIONS=0 -library=stlport4, \ CFLAGS_macosx := -DGTEST_OS_MAC=1, \ - CFLAGS_aix := -qpic=large, \ DISABLED_WARNINGS_gcc := undef, \ DISABLED_WARNINGS_clang := undef switch format-nonliteral \ tautological-undefined-compare $(BUILD_LIBJVM_DISABLED_WARNINGS_clang), \ @@ -88,7 +80,6 @@ $(eval $(call SetupNativeCompilation, BUILD_GTEST_LIBJVM, \ DISABLED_WARNINGS_CXX_microsoft := 4996, \ LDFLAGS := $(JVM_LDFLAGS), \ LDFLAGS_solaris := -library=stlport4 $(call SET_SHARED_LIBRARY_ORIGIN), \ - LDFLAGS_aix := -bbigtoc, \ LIBS := $(JVM_LIBS), \ OPTIMIZATION := $(JVM_OPTIMIZATION), \ MAPFILE := $(GTEST_JVM_MAPFILE), \ diff --git a/make/jdk/src/classes/build/tools/cldrconverter/NumberingSystemsParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/NumberingSystemsParseHandler.java index 11e43e49fc0..c2d468febe6 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/NumberingSystemsParseHandler.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/NumberingSystemsParseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -62,6 +62,9 @@ class NumberingSystemsParseHandler extends AbstractLDMLHandler { if (Character.isSurrogate(digits.charAt(0))) { // DecimalFormatSymbols doesn't support supplementary characters as digit zero. + // Replace supplementary digits with latin digits. This is a restriction till JDK-8204092 is resolved. + digits = "0123456789"; + put(script, digits); break numberingSystem; } // in case digits are in the reversed order, reverse back the order. diff --git a/make/nb_native/nbproject/configurations.xml b/make/nb_native/nbproject/configurations.xml index 43bcc96ffd0..6e5377c998f 100644 --- a/make/nb_native/nbproject/configurations.xml +++ b/make/nb_native/nbproject/configurations.xml @@ -6153,6 +6153,9 @@ libIsModifiableModuleTest.c + + libHeapMonitorTest.c + libMAAClassFileLoadHook.c @@ -40154,6 +40157,11 @@ tool="0" flavor2="0"> + + constant_encoding()); Address data_addr = __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, DataLayout::DataLayout::header_offset()), - LogBytesPerWord); - int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); - __ ldr(rscratch1, data_addr); - __ orr(rscratch1, rscratch1, header_bits); - __ str(rscratch1, data_addr); + md->byte_offset_of_slot(data, DataLayout::flags_offset()), + 0); + __ ldrb(rscratch1, data_addr); + __ orr(rscratch1, rscratch1, BitData::null_seen_byte_constant()); + __ strb(rscratch1, data_addr); __ b(*obj_is_null); __ bind(not_null); } else { @@ -1422,7 +1421,7 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L Address counter_addr = __ form_address(rscratch2, mdo, md->byte_offset_of_slot(data, CounterData::count_offset()), - LogBytesPerWord); + 0); __ ldr(rscratch1, counter_addr); __ sub(rscratch1, rscratch1, DataLayout::counter_increment); __ str(rscratch1, counter_addr); @@ -1471,12 +1470,11 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ mov_metadata(mdo, md->constant_encoding()); Address data_addr = __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, DataLayout::header_offset()), - LogBytesPerInt); - int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); - __ ldrw(rscratch1, data_addr); - __ orrw(rscratch1, rscratch1, header_bits); - __ strw(rscratch1, data_addr); + md->byte_offset_of_slot(data, DataLayout::flags_offset()), + 0); + __ ldrb(rscratch1, data_addr); + __ orr(rscratch1, rscratch1, BitData::null_seen_byte_constant()); + __ strb(rscratch1, data_addr); __ b(done); __ bind(not_null); } else { @@ -1880,7 +1878,7 @@ void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, // cpu register - cpu register Register reg2 = opr2->as_register(); if (opr1->type() == T_OBJECT || opr1->type() == T_ARRAY) { - __ cmp(reg1, reg2); + __ cmpoop(reg1, reg2); } else { assert(opr2->type() != T_OBJECT && opr2->type() != T_ARRAY, "cmp int, oop?"); __ cmpw(reg1, reg2); @@ -1911,8 +1909,9 @@ void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, break; case T_OBJECT: case T_ARRAY: - imm = jlong(opr2->as_constant_ptr()->as_jobject()); - break; + jobject2reg(opr2->as_constant_ptr()->as_jobject(), rscratch1); + __ cmpoop(reg1, rscratch1); + return; default: ShouldNotReachHere(); imm = 0; // unreachable diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index f7745c0e656..9a19ec6d6b0 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -109,6 +109,11 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } } +void BarrierSetAssembler::obj_equals(MacroAssembler* masm, + Register obj1, Register obj2) { + __ cmp(obj1, obj2); +} + void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath) { // If mask changes we need to ensure that the inverse is still encodable as an immediate diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp index 15229d4bf84..d0ec18c0521 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp @@ -40,6 +40,9 @@ public: virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2); + virtual void obj_equals(MacroAssembler* masm, + Register obj1, Register obj2); + virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index fc9aa31eba1..0930fc6eb1f 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -967,12 +967,11 @@ void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, int flag_byte_constant) { assert(ProfileInterpreter, "must be profiling interpreter"); - int header_offset = in_bytes(DataLayout::header_offset()); - int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); + int flags_offset = in_bytes(DataLayout::flags_offset()); // Set the flag - ldr(rscratch1, Address(mdp_in, header_offset)); - orr(rscratch1, rscratch1, header_bits); - str(rscratch1, Address(mdp_in, header_offset)); + ldrb(rscratch1, Address(mdp_in, flags_offset)); + orr(rscratch1, rscratch1, flag_byte_constant); + strb(rscratch1, Address(mdp_in, flags_offset)); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index b8dc219a1bb..66477e7774a 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -3651,6 +3651,11 @@ void MacroAssembler::cmpptr(Register src1, Address src2) { cmp(src1, rscratch1); } +void MacroAssembler::cmpoop(Register obj1, Register obj2) { + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, obj1, obj2); +} + void MacroAssembler::load_klass(Register dst, Register src) { if (UseCompressedClassPointers) { ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); @@ -5048,6 +5053,8 @@ void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, // a1 & a2 == 0 means (some-pointer is null) or // (very-rare-or-even-probably-impossible-pointer-values) // so, we can save one branch in most cases + cmpoop(a1, a2); + br(EQ, SAME); eor(rscratch1, a1, a2); tst(a1, a2); mov(result, false); @@ -5131,7 +5138,7 @@ void MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, // faster to perform another branch before comparing a1 and a2 cmp(cnt1, elem_per_word); br(LE, SHORT); // short or same - cmp(a1, a2); + cmpoop(a1, a2); br(EQ, SAME); ldr(tmp3, Address(pre(a1, base_offset))); cmp(cnt1, stubBytesThreshold); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 5c977eb3261..5192b2f817a 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -979,6 +979,8 @@ public: void addptr(const Address &dst, int32_t src); void cmpptr(Register src1, Address src2); + void cmpoop(Register obj1, Register obj2); + // Various forms of CAS void cmpxchg_obj_header(Register oldv, Register newv, Register obj, Register tmp, diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index 9e92b2d2f90..9cb3987da18 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -152,7 +152,7 @@ void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, // assert(sizeof(u2) == sizeof(Method::_size_of_parameters), ""); Label L; __ ldr(rscratch1, __ argument_address(temp2, -1)); - __ cmp(recv, rscratch1); + __ cmpoop(recv, rscratch1); __ br(Assembler::EQ, L); __ ldr(r0, __ argument_address(temp2, -1)); __ hlt(0); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 632ab3a0313..3f0f3f65b53 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -2027,7 +2027,7 @@ void TemplateTable::if_acmp(Condition cc) // assume branch is more often taken than not (loops use backward branches) Label not_taken; __ pop_ptr(r1); - __ cmp(r1, r0); + __ cmpoop(r1, r0); __ br(j_not(cc), not_taken); branch(false, false); __ bind(not_taken); diff --git a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp index 987fe52389a..fb9ad81a25d 100644 --- a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp +++ b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp @@ -3202,23 +3202,38 @@ void LIR_Assembler::leal(LIR_Opr addr_opr, LIR_Opr dest, LIR_PatchCode patch_cod const Register dest_reg = dest->as_pointer_register(); const Register base_reg = addr->base()->as_pointer_register(); - if (Assembler::is_simm13(addr->disp())) { - if (addr->index()->is_valid()) { - const Register index_reg = addr->index()->as_pointer_register(); - assert(index_reg != G3_scratch, "invariant"); - __ add(base_reg, addr->disp(), G3_scratch); - __ add(index_reg, G3_scratch, dest_reg); - } else { - __ add(base_reg, addr->disp(), dest_reg); - } - } else { - __ set(addr->disp(), G3_scratch); + if (patch_code != lir_patch_none) { + PatchingStub* patch = new PatchingStub(_masm, PatchingStub::access_field_id); + assert(addr->disp() != 0, "must have"); + assert(base_reg != G3_scratch, "invariant"); + __ patchable_set(0, G3_scratch); + patching_epilog(patch, patch_code, base_reg, info); + assert(dest_reg != G3_scratch, "invariant"); if (addr->index()->is_valid()) { const Register index_reg = addr->index()->as_pointer_register(); assert(index_reg != G3_scratch, "invariant"); __ add(index_reg, G3_scratch, G3_scratch); } __ add(base_reg, G3_scratch, dest_reg); + } else { + if (Assembler::is_simm13(addr->disp())) { + if (addr->index()->is_valid()) { + const Register index_reg = addr->index()->as_pointer_register(); + assert(index_reg != G3_scratch, "invariant"); + __ add(base_reg, addr->disp(), G3_scratch); + __ add(index_reg, G3_scratch, dest_reg); + } else { + __ add(base_reg, addr->disp(), dest_reg); + } + } else { + __ set(addr->disp(), G3_scratch); + if (addr->index()->is_valid()) { + const Register index_reg = addr->index()->as_pointer_register(); + assert(index_reg != G3_scratch, "invariant"); + __ add(index_reg, G3_scratch, G3_scratch); + } + __ add(base_reg, G3_scratch, dest_reg); + } } } diff --git a/src/hotspot/cpu/sparc/vm_version_ext_sparc.cpp b/src/hotspot/cpu/sparc/vm_version_ext_sparc.cpp index 1da30f2d176..d31262a0ff5 100644 --- a/src/hotspot/cpu/sparc/vm_version_ext_sparc.cpp +++ b/src/hotspot/cpu/sparc/vm_version_ext_sparc.cpp @@ -31,10 +31,13 @@ int VM_Version_Ext::_no_of_threads = 0; int VM_Version_Ext::_no_of_cores = 0; int VM_Version_Ext::_no_of_sockets = 0; +#if defined(SOLARIS) kid_t VM_Version_Ext::_kcid = -1; +#endif char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; char VM_Version_Ext::_cpu_desc[CPU_DETAILED_DESC_BUF_SIZE] = {0}; +#if defined(SOLARIS) // get cpu information. It takes into account if the kstat chain id // has been changed and update the info if necessary. bool VM_Version_Ext::initialize_cpu_information(void) { @@ -144,6 +147,13 @@ bool VM_Version_Ext::initialize_cpu_information(void) { kstat_close(kc); return true; } +#elif defined(LINUX) +// get cpu information. +bool VM_Version_Ext::initialize_cpu_information(void) { + // Not yet implemented. + return false; +} +#endif int VM_Version_Ext::number_of_threads(void) { initialize_cpu_information(); diff --git a/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp b/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp index f7e4368ccc9..767cc24cb67 100644 --- a/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp +++ b/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp @@ -27,8 +27,11 @@ #include "utilities/macros.hpp" #include "vm_version_sparc.hpp" + +#if defined(SOLARIS) #include #include +#endif #define CPU_INFO "cpu_info" #define CPU_TYPE "fpu_type" @@ -45,7 +48,9 @@ class VM_Version_Ext : public VM_Version { static int _no_of_threads; static int _no_of_cores; static int _no_of_sockets; +#if defined(SOLARIS) static kid_t _kcid; +#endif static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; static char _cpu_desc[CPU_DETAILED_DESC_BUF_SIZE]; diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 9250e474cba..accaf4bccc7 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3338,6 +3338,14 @@ void Assembler::orl(Address dst, Register src) { emit_operand(src, dst); } +void Assembler::orb(Address dst, int imm8) { + InstructionMark im(this); + prefix(dst); + emit_int8((unsigned char)0x80); + emit_operand(rcx, dst, 1); + emit_int8(imm8); +} + void Assembler::packuswb(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 48d6564f269..f55fa4214cd 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1536,6 +1536,8 @@ private: void orl(Register dst, Register src); void orl(Address dst, Register src); + void orb(Address dst, int imm8); + void orq(Address dst, int32_t imm32); void orq(Register dst, int32_t imm32); void orq(Register dst, Address src); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 281490d6b99..df0ce767118 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1682,9 +1682,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L // Object is null; update MDO and exit Register mdo = klass_RInfo; __ mov_metadata(mdo, md->constant_encoding()); - Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::header_offset())); - int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); - __ orl(data_addr, header_bits); + Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset())); + int header_bits = BitData::null_seen_byte_constant(); + __ orb(data_addr, header_bits); __ jmp(*obj_is_null); __ bind(not_null); } else { @@ -1828,9 +1828,9 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { // Object is null; update MDO and exit Register mdo = klass_RInfo; __ mov_metadata(mdo, md->constant_encoding()); - Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::header_offset())); - int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); - __ orl(data_addr, header_bits); + Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset())); + int header_bits = BitData::null_seen_byte_constant(); + __ orb(data_addr, header_bits); __ jmp(done); __ bind(not_null); } else { diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index df57d637d72..08b0a93e89f 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -187,6 +187,27 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } } +#ifndef _LP64 +void BarrierSetAssembler::obj_equals(MacroAssembler* masm, + Address obj1, jobject obj2) { + __ cmpoop_raw(obj1, obj2); +} + +void BarrierSetAssembler::obj_equals(MacroAssembler* masm, + Register obj1, jobject obj2) { + __ cmpoop_raw(obj1, obj2); +} +#endif +void BarrierSetAssembler::obj_equals(MacroAssembler* masm, + Register obj1, Address obj2) { + __ cmpptr(obj1, obj2); +} + +void BarrierSetAssembler::obj_equals(MacroAssembler* masm, + Register obj1, Register obj2) { + __ cmpptr(obj1, obj2); +} + void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath) { __ clear_jweak_tag(obj); diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp index cac146f29b0..7280361f05f 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp @@ -44,6 +44,18 @@ public: virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2); +#ifndef _LP64 + virtual void obj_equals(MacroAssembler* masm, + Address obj1, jobject obj2); + virtual void obj_equals(MacroAssembler* masm, + Register obj1, jobject obj2); +#endif + + virtual void obj_equals(MacroAssembler* masm, + Register obj1, Register obj2); + virtual void obj_equals(MacroAssembler* masm, + Register obj1, Address obj2); + // Support for jniFastGetField to try resolving a jobject/jweak in native virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index ce58234f382..423e1b1e478 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -150,6 +150,9 @@ define_pd_global(bool, ThreadLocalHandshakes, false); product(bool, UseUnalignedLoadStores, false, \ "Use SSE2 MOVDQU instruction for Arraycopy") \ \ + product(bool, UseXMMForObjInit, false, \ + "Use XMM/YMM MOVDQU instruction for Object Initialization") \ + \ product(bool, UseFastStosb, false, \ "Use fast-string operation for zeroing: rep stosb") \ \ diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 863607e8fef..29e56b838a3 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1432,10 +1432,10 @@ void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, int flag_byte_constant) { assert(ProfileInterpreter, "must be profiling interpreter"); - int header_offset = in_bytes(DataLayout::header_offset()); - int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); + int header_offset = in_bytes(DataLayout::flags_offset()); + int header_bits = flag_byte_constant; // Set the flag - orl(Address(mdp_in, header_offset), header_bits); + orb(Address(mdp_in, header_offset), header_bits); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index df90ffbfbe8..46381e4b83c 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -118,14 +118,24 @@ void MacroAssembler::cmpklass(Register src1, Metadata* obj) { cmp_literal32(src1, (int32_t)obj, metadata_Relocation::spec_for_immediate()); } -void MacroAssembler::cmpoop(Address src1, jobject obj) { +void MacroAssembler::cmpoop_raw(Address src1, jobject obj) { cmp_literal32(src1, (int32_t)obj, oop_Relocation::spec_for_immediate()); } -void MacroAssembler::cmpoop(Register src1, jobject obj) { +void MacroAssembler::cmpoop_raw(Register src1, jobject obj) { cmp_literal32(src1, (int32_t)obj, oop_Relocation::spec_for_immediate()); } +void MacroAssembler::cmpoop(Address src1, jobject obj) { + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, src1, obj); +} + +void MacroAssembler::cmpoop(Register src1, jobject obj) { + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, src1, obj); +} + void MacroAssembler::extend_sign(Register hi, Register lo) { // According to Intel Doc. AP-526, "Integer Divide", p.18. if (VM_Version::is_P6() && hi == rdx && lo == rax) { @@ -2785,17 +2795,20 @@ void MacroAssembler::cmpptr(Address src1, AddressLiteral src2) { } void MacroAssembler::cmpoop(Register src1, Register src2) { - cmpptr(src1, src2); + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, src1, src2); } void MacroAssembler::cmpoop(Register src1, Address src2) { - cmpptr(src1, src2); + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, src1, src2); } #ifdef _LP64 void MacroAssembler::cmpoop(Register src1, jobject src2) { movoop(rscratch1, src2); - cmpptr(src1, rscratch1); + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->obj_equals(this, src1, rscratch1); } #endif @@ -6777,7 +6790,59 @@ void MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_ } -void MacroAssembler::clear_mem(Register base, Register cnt, Register tmp, bool is_large) { +// clear memory of size 'cnt' qwords, starting at 'base' using XMM/YMM registers +void MacroAssembler::xmm_clear_mem(Register base, Register cnt, XMMRegister xtmp) { + // cnt - number of qwords (8-byte words). + // base - start address, qword aligned. + Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end; + if (UseAVX >= 2) { + vpxor(xtmp, xtmp, xtmp, AVX_256bit); + } else { + pxor(xtmp, xtmp); + } + jmp(L_zero_64_bytes); + + BIND(L_loop); + if (UseAVX >= 2) { + vmovdqu(Address(base, 0), xtmp); + vmovdqu(Address(base, 32), xtmp); + } else { + movdqu(Address(base, 0), xtmp); + movdqu(Address(base, 16), xtmp); + movdqu(Address(base, 32), xtmp); + movdqu(Address(base, 48), xtmp); + } + addptr(base, 64); + + BIND(L_zero_64_bytes); + subptr(cnt, 8); + jccb(Assembler::greaterEqual, L_loop); + addptr(cnt, 4); + jccb(Assembler::less, L_tail); + // Copy trailing 32 bytes + if (UseAVX >= 2) { + vmovdqu(Address(base, 0), xtmp); + } else { + movdqu(Address(base, 0), xtmp); + movdqu(Address(base, 16), xtmp); + } + addptr(base, 32); + subptr(cnt, 4); + + BIND(L_tail); + addptr(cnt, 4); + jccb(Assembler::lessEqual, L_end); + decrement(cnt); + + BIND(L_sloop); + movq(Address(base, 0), xtmp); + addptr(base, 8); + decrement(cnt); + jccb(Assembler::greaterEqual, L_sloop); + BIND(L_end); +} + +void MacroAssembler::clear_mem(Register base, Register cnt, Register tmp, XMMRegister xtmp, bool is_large) { // cnt - number of qwords (8-byte words). // base - start address, qword aligned. // is_large - if optimizers know cnt is larger than InitArrayShortSize @@ -6789,7 +6854,9 @@ void MacroAssembler::clear_mem(Register base, Register cnt, Register tmp, bool i Label DONE; - xorptr(tmp, tmp); + if (!is_large || !UseXMMForObjInit) { + xorptr(tmp, tmp); + } if (!is_large) { Label LOOP, LONG; @@ -6815,6 +6882,9 @@ void MacroAssembler::clear_mem(Register base, Register cnt, Register tmp, bool i if (UseFastStosb) { shlptr(cnt, 3); // convert to number of bytes rep_stosb(); + } else if (UseXMMForObjInit) { + movptr(tmp, base); + xmm_clear_mem(tmp, cnt, xtmp); } else { NOT_LP64(shlptr(cnt, 1);) // convert to number of 32-bit words for 32-bit VM rep_stos(); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 07659880c1f..bc647ff887c 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -741,11 +741,13 @@ class MacroAssembler: public Assembler { void cmpklass(Address dst, Metadata* obj); void cmpklass(Register dst, Metadata* obj); void cmpoop(Address dst, jobject obj); + void cmpoop_raw(Address dst, jobject obj); #endif // _LP64 void cmpoop(Register src1, Register src2); void cmpoop(Register src1, Address src2); void cmpoop(Register dst, jobject obj); + void cmpoop_raw(Register dst, jobject obj); // NOTE src2 must be the lval. This is NOT an mem-mem compare void cmpptr(Address src1, AddressLiteral src2); @@ -1578,7 +1580,10 @@ public: // clear memory of size 'cnt' qwords, starting at 'base'; // if 'is_large' is set, do not try to produce short loop - void clear_mem(Register base, Register cnt, Register rtmp, bool is_large); + void clear_mem(Register base, Register cnt, Register rtmp, XMMRegister xtmp, bool is_large); + + // clear memory of size 'cnt' qwords, starting at 'base' using XMM/YMM registers + void xmm_clear_mem(Register base, Register cnt, XMMRegister xtmp); #ifdef COMPILER2 void string_indexof_char(Register str1, Register cnt1, Register ch, Register result, diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index f1565b77cd9..7028cd9dab5 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1396,6 +1396,16 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseFastStosb, false); } + // Use XMM/YMM MOVDQU instruction for Object Initialization + if (!UseFastStosb && UseSSE >= 2 && UseUnalignedLoadStores) { + if (FLAG_IS_DEFAULT(UseXMMForObjInit)) { + UseXMMForObjInit = true; + } + } else if (UseXMMForObjInit) { + warning("UseXMMForObjInit requires SSE2 and unaligned load/stores. Feature is switched off."); + FLAG_SET_DEFAULT(UseXMMForObjInit, false); + } + #ifdef COMPILER2 if (FLAG_IS_DEFAULT(AlignVector)) { // Modern processors allow misaligned memory operations for vectors. diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 22ecc6f095f..449f65e1f06 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -11482,10 +11482,10 @@ instruct MoveL2D_reg_reg_sse(regD dst, eRegL src, regD tmp) %{ // ======================================================================= // fast clearing of an array -instruct rep_stos(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy, eFlagsReg cr) %{ +instruct rep_stos(eCXRegI cnt, eDIRegP base, regD tmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{ predicate(!((ClearArrayNode*)n)->is_large()); match(Set dummy (ClearArray cnt base)); - effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr); format %{ $$template $$emit$$"XOR EAX,EAX\t# ClearArray:\n\t" @@ -11502,6 +11502,32 @@ instruct rep_stos(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy, eFlag if (UseFastStosb) { $$emit$$"SHL ECX,3\t# Convert doublewords to bytes\n\t" $$emit$$"REP STOSB\t# store EAX into [EDI++] while ECX--\n\t" + } else if (UseXMMForObjInit) { + $$emit$$"MOV RDI,RAX\n\t" + $$emit$$"VPXOR YMM0,YMM0,YMM0\n\t" + $$emit$$"JMPQ L_zero_64_bytes\n\t" + $$emit$$"# L_loop:\t# 64-byte LOOP\n\t" + $$emit$$"VMOVDQU YMM0,(RAX)\n\t" + $$emit$$"VMOVDQU YMM0,0x20(RAX)\n\t" + $$emit$$"ADD 0x40,RAX\n\t" + $$emit$$"# L_zero_64_bytes:\n\t" + $$emit$$"SUB 0x8,RCX\n\t" + $$emit$$"JGE L_loop\n\t" + $$emit$$"ADD 0x4,RCX\n\t" + $$emit$$"JL L_tail\n\t" + $$emit$$"VMOVDQU YMM0,(RAX)\n\t" + $$emit$$"ADD 0x20,RAX\n\t" + $$emit$$"SUB 0x4,RCX\n\t" + $$emit$$"# L_tail:\t# Clearing tail bytes\n\t" + $$emit$$"ADD 0x4,RCX\n\t" + $$emit$$"JLE L_end\n\t" + $$emit$$"DEC RCX\n\t" + $$emit$$"# L_sloop:\t# 8-byte short loop\n\t" + $$emit$$"VMOVQ XMM0,(RAX)\n\t" + $$emit$$"ADD 0x8,RAX\n\t" + $$emit$$"DEC RCX\n\t" + $$emit$$"JGE L_sloop\n\t" + $$emit$$"# L_end:\n\t" } else { $$emit$$"SHL ECX,1\t# Convert doublewords to words\n\t" $$emit$$"REP STOS\t# store EAX into [EDI++] while ECX--\n\t" @@ -11509,28 +11535,57 @@ instruct rep_stos(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy, eFlag $$emit$$"# DONE" %} ins_encode %{ - __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, false); + __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, + $tmp$$XMMRegister, false); %} ins_pipe( pipe_slow ); %} -instruct rep_stos_large(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy, eFlagsReg cr) %{ +instruct rep_stos_large(eCXRegI cnt, eDIRegP base, regD tmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{ predicate(((ClearArrayNode*)n)->is_large()); match(Set dummy (ClearArray cnt base)); - effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr); format %{ $$template - $$emit$$"XOR EAX,EAX\t# ClearArray:\n\t" if (UseFastStosb) { + $$emit$$"XOR EAX,EAX\t# ClearArray:\n\t" $$emit$$"SHL ECX,3\t# Convert doublewords to bytes\n\t" $$emit$$"REP STOSB\t# store EAX into [EDI++] while ECX--\n\t" + } else if (UseXMMForObjInit) { + $$emit$$"MOV RDI,RAX\t# ClearArray:\n\t" + $$emit$$"VPXOR YMM0,YMM0,YMM0\n\t" + $$emit$$"JMPQ L_zero_64_bytes\n\t" + $$emit$$"# L_loop:\t# 64-byte LOOP\n\t" + $$emit$$"VMOVDQU YMM0,(RAX)\n\t" + $$emit$$"VMOVDQU YMM0,0x20(RAX)\n\t" + $$emit$$"ADD 0x40,RAX\n\t" + $$emit$$"# L_zero_64_bytes:\n\t" + $$emit$$"SUB 0x8,RCX\n\t" + $$emit$$"JGE L_loop\n\t" + $$emit$$"ADD 0x4,RCX\n\t" + $$emit$$"JL L_tail\n\t" + $$emit$$"VMOVDQU YMM0,(RAX)\n\t" + $$emit$$"ADD 0x20,RAX\n\t" + $$emit$$"SUB 0x4,RCX\n\t" + $$emit$$"# L_tail:\t# Clearing tail bytes\n\t" + $$emit$$"ADD 0x4,RCX\n\t" + $$emit$$"JLE L_end\n\t" + $$emit$$"DEC RCX\n\t" + $$emit$$"# L_sloop:\t# 8-byte short loop\n\t" + $$emit$$"VMOVQ XMM0,(RAX)\n\t" + $$emit$$"ADD 0x8,RAX\n\t" + $$emit$$"DEC RCX\n\t" + $$emit$$"JGE L_sloop\n\t" + $$emit$$"# L_end:\n\t" } else { + $$emit$$"XOR EAX,EAX\t# ClearArray:\n\t" $$emit$$"SHL ECX,1\t# Convert doublewords to words\n\t" $$emit$$"REP STOS\t# store EAX into [EDI++] while ECX--\n\t" } $$emit$$"# DONE" %} ins_encode %{ - __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, true); + __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, + $tmp$$XMMRegister, true); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 368a5b03faf..5d1b9318adb 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -10770,12 +10770,12 @@ instruct MoveL2D_reg_reg(regD dst, rRegL src) %{ // ======================================================================= // fast clearing of an array -instruct rep_stos(rcx_RegL cnt, rdi_RegP base, rax_RegI zero, Universe dummy, - rFlagsReg cr) +instruct rep_stos(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero, + Universe dummy, rFlagsReg cr) %{ predicate(!((ClearArrayNode*)n)->is_large()); match(Set dummy (ClearArray cnt base)); - effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr); format %{ $$template $$emit$$"xorq rax, rax\t# ClearArray:\n\t" @@ -10791,35 +10791,90 @@ instruct rep_stos(rcx_RegL cnt, rdi_RegP base, rax_RegI zero, Universe dummy, if (UseFastStosb) { $$emit$$"shlq rcx,3\t# Convert doublewords to bytes\n\t" $$emit$$"rep stosb\t# Store rax to *rdi++ while rcx--\n\t" + } else if (UseXMMForObjInit) { + $$emit$$"mov rdi,rax\n\t" + $$emit$$"vpxor ymm0,ymm0,ymm0\n\t" + $$emit$$"jmpq L_zero_64_bytes\n\t" + $$emit$$"# L_loop:\t# 64-byte LOOP\n\t" + $$emit$$"vmovdqu ymm0,(rax)\n\t" + $$emit$$"vmovdqu ymm0,0x20(rax)\n\t" + $$emit$$"add 0x40,rax\n\t" + $$emit$$"# L_zero_64_bytes:\n\t" + $$emit$$"sub 0x8,rcx\n\t" + $$emit$$"jge L_loop\n\t" + $$emit$$"add 0x4,rcx\n\t" + $$emit$$"jl L_tail\n\t" + $$emit$$"vmovdqu ymm0,(rax)\n\t" + $$emit$$"add 0x20,rax\n\t" + $$emit$$"sub 0x4,rcx\n\t" + $$emit$$"# L_tail:\t# Clearing tail bytes\n\t" + $$emit$$"add 0x4,rcx\n\t" + $$emit$$"jle L_end\n\t" + $$emit$$"dec rcx\n\t" + $$emit$$"# L_sloop:\t# 8-byte short loop\n\t" + $$emit$$"vmovq xmm0,(rax)\n\t" + $$emit$$"add 0x8,rax\n\t" + $$emit$$"dec rcx\n\t" + $$emit$$"jge L_sloop\n\t" + $$emit$$"# L_end:\n\t" } else { $$emit$$"rep stosq\t# Store rax to *rdi++ while rcx--\n\t" } $$emit$$"# DONE" %} ins_encode %{ - __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, false); + __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, + $tmp$$XMMRegister, false); %} ins_pipe(pipe_slow); %} -instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, rax_RegI zero, Universe dummy, - rFlagsReg cr) +instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero, + Universe dummy, rFlagsReg cr) %{ predicate(((ClearArrayNode*)n)->is_large()); match(Set dummy (ClearArray cnt base)); - effect(USE_KILL cnt, USE_KILL base, KILL zero, KILL cr); + effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr); format %{ $$template - $$emit$$"xorq rax, rax\t# ClearArray:\n\t" if (UseFastStosb) { + $$emit$$"xorq rax, rax\t# ClearArray:\n\t" $$emit$$"shlq rcx,3\t# Convert doublewords to bytes\n\t" $$emit$$"rep stosb\t# Store rax to *rdi++ while rcx--" + } else if (UseXMMForObjInit) { + $$emit$$"mov rdi,rax\t# ClearArray:\n\t" + $$emit$$"vpxor ymm0,ymm0,ymm0\n\t" + $$emit$$"jmpq L_zero_64_bytes\n\t" + $$emit$$"# L_loop:\t# 64-byte LOOP\n\t" + $$emit$$"vmovdqu ymm0,(rax)\n\t" + $$emit$$"vmovdqu ymm0,0x20(rax)\n\t" + $$emit$$"add 0x40,rax\n\t" + $$emit$$"# L_zero_64_bytes:\n\t" + $$emit$$"sub 0x8,rcx\n\t" + $$emit$$"jge L_loop\n\t" + $$emit$$"add 0x4,rcx\n\t" + $$emit$$"jl L_tail\n\t" + $$emit$$"vmovdqu ymm0,(rax)\n\t" + $$emit$$"add 0x20,rax\n\t" + $$emit$$"sub 0x4,rcx\n\t" + $$emit$$"# L_tail:\t# Clearing tail bytes\n\t" + $$emit$$"add 0x4,rcx\n\t" + $$emit$$"jle L_end\n\t" + $$emit$$"dec rcx\n\t" + $$emit$$"# L_sloop:\t# 8-byte short loop\n\t" + $$emit$$"vmovq xmm0,(rax)\n\t" + $$emit$$"add 0x8,rax\n\t" + $$emit$$"dec rcx\n\t" + $$emit$$"jge L_sloop\n\t" + $$emit$$"# L_end:\n\t" } else { + $$emit$$"xorq rax, rax\t# ClearArray:\n\t" $$emit$$"rep stosq\t# Store rax to *rdi++ while rcx--" } %} ins_encode %{ - __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, true); + __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register, + $tmp$$XMMRegister, true); %} ins_pipe(pipe_slow); %} diff --git a/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.cpp b/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.cpp index a975b668acc..0d33ce36d15 100644 --- a/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.cpp +++ b/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.cpp @@ -35,6 +35,13 @@ frame JavaThread::pd_last_frame() { return frame(last_Java_sp(), frame::unpatchable, _anchor.last_Java_pc()); } +bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) { + ucontext_t* uc = (ucontext_t*) ucontext; + *fr_addr = frame((intptr_t*)uc->uc_mcontext.mc_i7, frame::unpatchable, + (address)uc->uc_mcontext.mc_gregs[MC_PC]); + return true; +} + // For Forte Analyzer AsyncGetCallTrace profiling support - thread is // currently interrupted by SIGPROF bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, diff --git a/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.hpp b/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.hpp index ab25fd874c1..43959b26f69 100644 --- a/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.hpp +++ b/src/hotspot/os_cpu/linux_sparc/thread_linux_sparc.hpp @@ -85,8 +85,9 @@ public: _base_of_stack_pointer = sp; } - bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, - bool isInJava); + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava); + + bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); // These routines are only used on cpu architectures that // have separate register stacks (Itanium). diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 2fe395c23f2..073d1b9fd92 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -3245,7 +3245,7 @@ void LIRGenerator::do_ProfileInvoke(ProfileInvoke* x) { int freq_log = Tier23InlineeNotifyFreqLog; double scale; if (_method->has_option_value("CompileThresholdScaling", scale)) { - freq_log = Arguments::scaled_freq_log(freq_log, scale); + freq_log = CompilerConfig::scaled_freq_log(freq_log, scale); } increment_event_counter_impl(info, x->inlinee(), LIR_OprFact::intConst(InvocationCounter::count_increment), right_n_bits(freq_log), InvocationEntryBci, false, true); } @@ -3279,7 +3279,7 @@ void LIRGenerator::increment_event_counter(CodeEmitInfo* info, LIR_Opr step, int // Increment the appropriate invocation/backedge counter and notify the runtime. double scale; if (_method->has_option_value("CompileThresholdScaling", scale)) { - freq_log = Arguments::scaled_freq_log(freq_log, scale); + freq_log = CompilerConfig::scaled_freq_log(freq_log, scale); } increment_event_counter_impl(info, info->scope()->method(), step, right_n_bits(freq_log), bci, backedge, true); } diff --git a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp index 4a5fc402d1a..e79b33877ab 100644 --- a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp +++ b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp @@ -30,6 +30,7 @@ #include "memory/allocation.hpp" #include "memory/resourceArea.hpp" #include "runtime/safepoint.hpp" +#include "oops/reflectionAccessorImplKlassHelper.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -202,9 +203,11 @@ class LoaderTreeNode : public ResourceObj { } if (print_classes) { - if (_classes != NULL) { for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) { + // Non-anonymous classes should live in the primary CLD of its loader + assert(lci->_cld == _cld, "must be"); + branchtracker.print(st); if (lci == _classes) { // first iteration st->print("%*s ", indentation, "Classes:"); @@ -212,9 +215,15 @@ class LoaderTreeNode : public ResourceObj { st->print("%*s ", indentation, ""); } st->print("%s", lci->_klass->external_name()); + + // Special treatment for generated core reflection accessor classes: print invocation target. + if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) { + st->print(" (invokes: "); + ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass); + st->print(")"); + } + st->cr(); - // Non-anonymous classes should live in the primary CLD of its loader - assert(lci->_cld == _cld, "must be"); } branchtracker.print(st); st->print("%*s ", indentation, ""); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index de77baea739..ff31188b685 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -63,7 +63,6 @@ #include "runtime/vframe.inline.hpp" #include "utilities/align.hpp" #include "utilities/preserveException.hpp" - #if INCLUDE_JVMCI #include "jvmci/jvmciJavaClasses.hpp" #endif @@ -798,7 +797,7 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) { // During bootstrap, java.lang.Class wasn't loaded so static field // offsets were computed without the size added it. Go back and // update all the static field offsets to included the size. - for (JavaFieldStream fs(InstanceKlass::cast(k)); !fs.done(); fs.next()) { + for (JavaFieldStream fs(InstanceKlass::cast(k)); !fs.done(); fs.next()) { if (fs.access_flags().is_static()) { int real_offset = fs.offset() + InstanceMirrorKlass::offset_of_static_fields(); fs.set_offset(real_offset); @@ -809,12 +808,8 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) { if (k->is_shared() && k->has_raw_archived_mirror()) { if (MetaspaceShared::open_archive_heap_region_mapped()) { - oop m = k->archived_java_mirror(); - assert(m != NULL, "archived mirror is NULL"); - assert(MetaspaceShared::is_archive_object(m), "must be archived mirror object"); - Handle m_h(THREAD, m); - // restore_archived_mirror() clears the klass' _has_raw_archived_mirror flag - restore_archived_mirror(k, m_h, Handle(), Handle(), Handle(), CHECK); + bool present = restore_archived_mirror(k, Handle(), Handle(), Handle(), CHECK); + assert(present, "Missing archived mirror for %s", k->external_name()); return; } else { k->set_java_mirror_handle(NULL); @@ -1207,11 +1202,23 @@ oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror, return archived_mirror; } -// After the archived mirror object is restored, the shared klass' -// _has_raw_archived_mirror flag is cleared -void java_lang_Class::restore_archived_mirror(Klass *k, Handle mirror, +// Returns true if the mirror is updated, false if no archived mirror +// data is present. After the archived mirror object is restored, the +// shared klass' _has_raw_archived_mirror flag is cleared. +bool java_lang_Class::restore_archived_mirror(Klass *k, Handle class_loader, Handle module, Handle protection_domain, TRAPS) { + oop m = MetaspaceShared::materialize_archived_object(k->archived_java_mirror_raw()); + + if (m == NULL) { + return false; + } + + log_debug(cds, mirror)("Archived mirror is: " PTR_FORMAT, p2i(m)); + + // mirror is archived, restore + assert(MetaspaceShared::is_archive_object(m), "must be archived mirror object"); + Handle mirror(THREAD, m); // The java.lang.Class field offsets were archived and reloaded from archive. // No need to put classes on the fixup_mirror_list before java.lang.Class @@ -1221,7 +1228,7 @@ void java_lang_Class::restore_archived_mirror(Klass *k, Handle mirror, // - local static final fields with initial values were initialized at dump time // create the init_lock - typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK); + typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_(false)); set_init_lock(mirror(), r); if (protection_domain.not_null()) { @@ -1241,6 +1248,8 @@ void java_lang_Class::restore_archived_mirror(Klass *k, Handle mirror, ResourceMark rm; log_trace(cds, mirror)("Restored %s archived mirror " PTR_FORMAT, k->external_name(), p2i(mirror())); + + return true; } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index cf21158df68..7f1d7cc9b12 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -229,8 +229,9 @@ class java_lang_Class : AllStatic { static oop archive_mirror(Klass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(NULL); static oop process_archived_mirror(Klass* k, oop mirror, oop archived_mirror, Thread *THREAD) NOT_CDS_JAVA_HEAP_RETURN_(NULL); - static void restore_archived_mirror(Klass *k, Handle mirror, Handle class_loader, Handle module, - Handle protection_domain, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; + static bool restore_archived_mirror(Klass *k, Handle class_loader, Handle module, + Handle protection_domain, + TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false); static void fixup_module_field(Klass* k, Handle module); diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 987a0fe7eaa..d4965bb3fa0 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -56,15 +56,33 @@ void ModuleEntry::set_location(Symbol* location) { } } -bool ModuleEntry::is_non_jdk_module() { - ResourceMark rm; +// Return true if the module's version should be displayed in error messages, +// logging, etc. +// Return false if the module's version is null, if it is unnamed, or if the +// module is not an upgradeable module. +// Detect if the module is not upgradeable by checking: +// 1. Module location is "jrt:/java." and its loader is boot or platform +// 2. Module location is "jrt:/jdk.", its loader is one of the builtin loaders +// and its version is the same as module java.base's version +// The above check is imprecise but should work in almost all cases. +bool ModuleEntry::should_show_version() { + if (version() == NULL || !is_named()) return false; + if (location() != NULL) { + ResourceMark rm; const char* loc = location()->as_C_string(); - if (strncmp(loc, "jrt:/java.", 10) != 0 && strncmp(loc, "jrt:/jdk.", 9) != 0) { - return true; + ClassLoaderData* cld = loader_data(); + + if ((cld->is_the_null_class_loader_data() || cld->is_platform_class_loader_data()) && + (strncmp(loc, "jrt:/java.", 10) == 0)) { + return false; + } + if ((ModuleEntryTable::javabase_moduleEntry()->version()->fast_compare(version()) == 0) && + cld->is_permanent_class_loader_data() && (strncmp(loc, "jrt:/jdk.", 9) == 0)) { + return false; } } - return false; + return true; } void ModuleEntry::set_version(Symbol* version) { diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 232b2644f3c..a89131fdfc0 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -117,7 +117,7 @@ public: Symbol* location() const { return _location; } void set_location(Symbol* location); - bool is_non_jdk_module(); + bool should_show_version(); bool can_read(ModuleEntry* m) const; bool has_reads_list() const; diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index fc696792e7d..93711919a0b 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -198,17 +198,16 @@ size_t StringTable::item_added() { return Atomic::add((size_t)1, &(the_table()->_items)); } -size_t StringTable::items_to_clean(size_t ncl) { - size_t total = Atomic::add((size_t)ncl, &(the_table()->_uncleaned_items)); +size_t StringTable::add_items_to_clean(size_t ndead) { + size_t total = Atomic::add((size_t)ndead, &(the_table()->_uncleaned_items)); log_trace(stringtable)( "Uncleaned items:" SIZE_FORMAT " added: " SIZE_FORMAT " total:" SIZE_FORMAT, - the_table()->_uncleaned_items, ncl, total); + the_table()->_uncleaned_items, ndead, total); return total; } void StringTable::item_removed() { Atomic::add((size_t)-1, &(the_table()->_items)); - Atomic::add((size_t)-1, &(the_table()->_uncleaned_items)); } double StringTable::get_load_factor() { @@ -405,8 +404,11 @@ void StringTable::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, StringTable::the_table()->_weak_handles->weak_oops_do(&stiac, tmp); - StringTable::the_table()->items_to_clean(stiac._count); + // This is the serial case without ParState. + // Just set the correct number and check for a cleaning phase. + the_table()->_uncleaned_items = stiac._count; StringTable::the_table()->check_concurrent_work(); + if (processed != NULL) { *processed = (int) stiac._count_total; } @@ -430,8 +432,9 @@ void StringTable::possibly_parallel_unlink( _par_state_string->weak_oops_do(&stiac, &dnc); - StringTable::the_table()->items_to_clean(stiac._count); - StringTable::the_table()->check_concurrent_work(); + // Accumulate the dead strings. + the_table()->add_items_to_clean(stiac._count); + *processed = (int) stiac._count_total; *removed = (int) stiac._count; } @@ -467,10 +470,8 @@ void StringTable::grow(JavaThread* jt) { } struct StringTableDoDelete : StackObj { - long _count; - StringTableDoDelete() : _count(0) {} void operator()(WeakHandle* val) { - ++_count; + /* do nothing */ } }; @@ -524,6 +525,7 @@ void StringTable::check_concurrent_work() { if (_has_work) { return; } + double load_factor = StringTable::get_load_factor(); double dead_factor = StringTable::get_dead_factor(); // We should clean/resize if we have more dead than alive, diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index ca056949930..1cb62aa3935 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.hpp @@ -83,7 +83,7 @@ private: static uintx item_added(); static void item_removed(); - static size_t items_to_clean(size_t ncl); + size_t add_items_to_clean(size_t ndead); StringTable(); @@ -113,6 +113,23 @@ private: static bool has_work() { return the_table()->_has_work; } // GC support + + // Must be called before a parallel walk where strings might die. + static void reset_dead_counter() { + the_table()->_uncleaned_items = 0; + } + // After the parallel walk this method must be called to trigger + // cleaning. Note it might trigger a resize instead. + static void finish_dead_counter() { + the_table()->check_concurrent_work(); + } + + // If GC uses ParState directly it should add the number of cleared + // strings to this method. + static void inc_dead_counter(size_t ndead) { + the_table()->add_items_to_clean(ndead); + } + // Delete pointers to otherwise-unreachable objects. static void unlink(BoolObjectClosure* cl) { unlink_or_oops_do(cl); @@ -150,9 +167,9 @@ private: oop lookup_shared(jchar* name, int len, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(NULL); static void copy_shared_string_table(CompactStringTableWriter* ch_table) NOT_CDS_JAVA_HEAP_RETURN; public: - static oop create_archived_string(oop s, Thread* THREAD); + static oop create_archived_string(oop s, Thread* THREAD) NOT_CDS_JAVA_HEAP_RETURN_(NULL); static void set_shared_string_mapped() { _shared_string_mapped = true; } - static bool shared_string_mapped() { return _shared_string_mapped; } + static bool shared_string_mapped() { return _shared_string_mapped; } static void shared_oops_do(OopClosure* f) NOT_CDS_JAVA_HEAP_RETURN; static void write_to_archive() NOT_CDS_JAVA_HEAP_RETURN; static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index f9f22a68649..3746e03ab6e 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -374,7 +374,6 @@ template(fillInStackTrace_name, "fillInStackTrace") \ template(getCause_name, "getCause") \ template(initCause_name, "initCause") \ - template(setProperty_name, "setProperty") \ template(getProperty_name, "getProperty") \ template(context_name, "context") \ template(contextClassLoader_name, "contextClassLoader") \ diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 0139ceb25f3..7c1e31c4c6f 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -26,6 +26,8 @@ #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" #include "compiler/compilerDefinitions.hpp" +#include "gc/shared/gcConfig.hpp" +#include "utilities/defaultStream.hpp" const char* compilertype2name_tab[compiler_number_of_types] = { "", @@ -60,6 +62,55 @@ CompMode Compilation_mode = CompMode_client; CompMode Compilation_mode = CompMode_none; #endif +// Returns threshold scaled with CompileThresholdScaling +intx CompilerConfig::scaled_compile_threshold(intx threshold) { + return scaled_compile_threshold(threshold, CompileThresholdScaling); +} + +// Returns freq_log scaled with CompileThresholdScaling +intx CompilerConfig::scaled_freq_log(intx freq_log) { + return scaled_freq_log(freq_log, CompileThresholdScaling); +} + +// Returns threshold scaled with the value of scale. +// If scale < 0.0, threshold is returned without scaling. +intx CompilerConfig::scaled_compile_threshold(intx threshold, double scale) { + if (scale == 1.0 || scale < 0.0) { + return threshold; + } else { + return (intx)(threshold * scale); + } +} + +// Returns freq_log scaled with the value of scale. +// Returned values are in the range of [0, InvocationCounter::number_of_count_bits + 1]. +// If scale < 0.0, freq_log is returned without scaling. +intx CompilerConfig::scaled_freq_log(intx freq_log, double scale) { + // Check if scaling is necessary or if negative value was specified. + if (scale == 1.0 || scale < 0.0) { + return freq_log; + } + // Check values to avoid calculating log2 of 0. + if (scale == 0.0 || freq_log == 0) { + return 0; + } + // Determine the maximum notification frequency value currently supported. + // The largest mask value that the interpreter/C1 can handle is + // of length InvocationCounter::number_of_count_bits. Mask values are always + // one bit shorter then the value of the notification frequency. Set + // max_freq_bits accordingly. + intx max_freq_bits = InvocationCounter::number_of_count_bits + 1; + intx scaled_freq = scaled_compile_threshold((intx)1 << freq_log, scale); + if (scaled_freq == 0) { + // Return 0 right away to avoid calculating log2 of 0. + return 0; + } else if (scaled_freq > nth_bit(max_freq_bits)) { + return max_freq_bits; + } else { + return log2_intptr(scaled_freq); + } +} + #ifdef TIERED void set_client_compilation_mode() { Compilation_mode = CompMode_client; @@ -113,4 +164,284 @@ void set_client_compilation_mode() { FLAG_SET_ERGO(intx, CICompilerCount, 1); } } + +bool compilation_mode_selected() { + return !FLAG_IS_DEFAULT(TieredCompilation) || + !FLAG_IS_DEFAULT(TieredStopAtLevel) || + !FLAG_IS_DEFAULT(UseAOT) + JVMCI_ONLY(|| !FLAG_IS_DEFAULT(EnableJVMCI) + || !FLAG_IS_DEFAULT(UseJVMCICompiler)); +} + +void select_compilation_mode_ergonomically() { +#if defined(_WINDOWS) && !defined(_LP64) + if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { + FLAG_SET_ERGO(bool, NeverActAsServerClassMachine, true); + } +#endif + if (NeverActAsServerClassMachine) { + set_client_compilation_mode(); + } +} + #endif // TIERED + +void CompilerConfig::set_tiered_flags() { + // With tiered, set default policy to SimpleThresholdPolicy, which is 2. + if (FLAG_IS_DEFAULT(CompilationPolicyChoice)) { + FLAG_SET_DEFAULT(CompilationPolicyChoice, 2); + } + if (CompilationPolicyChoice < 2) { + vm_exit_during_initialization( + "Incompatible compilation policy selected", NULL); + } + // Increase the code cache size - tiered compiles a lot more. + if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { + FLAG_SET_ERGO(uintx, ReservedCodeCacheSize, + MIN2(CODE_CACHE_DEFAULT_LIMIT, ReservedCodeCacheSize * 5)); + } + // Enable SegmentedCodeCache if TieredCompilation is enabled and ReservedCodeCacheSize >= 240M + if (FLAG_IS_DEFAULT(SegmentedCodeCache) && ReservedCodeCacheSize >= 240*M) { + FLAG_SET_ERGO(bool, SegmentedCodeCache, true); + } + if (!UseInterpreter) { // -Xcomp + Tier3InvokeNotifyFreqLog = 0; + Tier4InvocationThreshold = 0; + } + + if (CompileThresholdScaling < 0) { + vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", NULL); + } + + // Scale tiered compilation thresholds. + // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves compilation thresholds unchanged. + if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { + FLAG_SET_ERGO(intx, Tier0InvokeNotifyFreqLog, scaled_freq_log(Tier0InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier0BackedgeNotifyFreqLog, scaled_freq_log(Tier0BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier3InvocationThreshold, scaled_compile_threshold(Tier3InvocationThreshold)); + FLAG_SET_ERGO(intx, Tier3MinInvocationThreshold, scaled_compile_threshold(Tier3MinInvocationThreshold)); + FLAG_SET_ERGO(intx, Tier3CompileThreshold, scaled_compile_threshold(Tier3CompileThreshold)); + FLAG_SET_ERGO(intx, Tier3BackEdgeThreshold, scaled_compile_threshold(Tier3BackEdgeThreshold)); + + // Tier2{Invocation,MinInvocation,Compile,Backedge}Threshold should be scaled here + // once these thresholds become supported. + + FLAG_SET_ERGO(intx, Tier2InvokeNotifyFreqLog, scaled_freq_log(Tier2InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier2BackedgeNotifyFreqLog, scaled_freq_log(Tier2BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier3InvokeNotifyFreqLog, scaled_freq_log(Tier3InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier3BackedgeNotifyFreqLog, scaled_freq_log(Tier3BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier23InlineeNotifyFreqLog, scaled_freq_log(Tier23InlineeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier4InvocationThreshold, scaled_compile_threshold(Tier4InvocationThreshold)); + FLAG_SET_ERGO(intx, Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold)); + FLAG_SET_ERGO(intx, Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold)); + FLAG_SET_ERGO(intx, Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold)); + } +} + +#if INCLUDE_JVMCI +void set_jvmci_specific_flags() { + if (UseJVMCICompiler) { + Compilation_mode = CompMode_server; + + if (FLAG_IS_DEFAULT(TypeProfileWidth)) { + FLAG_SET_DEFAULT(TypeProfileWidth, 8); + } + if (FLAG_IS_DEFAULT(OnStackReplacePercentage)) { + FLAG_SET_DEFAULT(OnStackReplacePercentage, 933); + } + if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { + FLAG_SET_DEFAULT(ReservedCodeCacheSize, 64*M); + } + if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { + FLAG_SET_DEFAULT(InitialCodeCacheSize, 16*M); + } + if (FLAG_IS_DEFAULT(MetaspaceSize)) { + FLAG_SET_DEFAULT(MetaspaceSize, 12*M); + } + if (FLAG_IS_DEFAULT(NewSizeThreadIncrease)) { + FLAG_SET_DEFAULT(NewSizeThreadIncrease, 4*K); + } + if (TieredStopAtLevel != CompLevel_full_optimization) { + // Currently JVMCI compiler can only work at the full optimization level + warning("forcing TieredStopAtLevel to full optimization because JVMCI is enabled"); + FLAG_SET_ERGO(intx, TieredStopAtLevel, CompLevel_full_optimization); + } + if (FLAG_IS_DEFAULT(TypeProfileLevel)) { + FLAG_SET_DEFAULT(TypeProfileLevel, 0); + } + } +} +#endif // INCLUDE_JVMCI + +bool CompilerConfig::check_args_consistency(bool status) { + // 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 (ReservedCodeCacheSize < InitialCodeCacheSize) { + jio_fprintf(defaultStream::error_stream(), + "Invalid ReservedCodeCacheSize: %dK. Must be at least InitialCodeCacheSize=%dK.\n", + ReservedCodeCacheSize/K, InitialCodeCacheSize/K); + status = false; + } else if (ReservedCodeCacheSize < min_code_cache_size) { + jio_fprintf(defaultStream::error_stream(), + "Invalid ReservedCodeCacheSize=%dK. Must be at least %uK.\n", ReservedCodeCacheSize/K, + min_code_cache_size/K); + status = false; + } else if (ReservedCodeCacheSize > CODE_CACHE_SIZE_LIMIT) { + // Code cache size larger than CODE_CACHE_SIZE_LIMIT is not supported. + jio_fprintf(defaultStream::error_stream(), + "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M, + CODE_CACHE_SIZE_LIMIT/M); + status = false; + } else if (NonNMethodCodeHeapSize < min_code_cache_size) { + jio_fprintf(defaultStream::error_stream(), + "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K, + min_code_cache_size/K); + status = false; + } + +#ifdef _LP64 + if (!FLAG_IS_DEFAULT(CICompilerCount) && !FLAG_IS_DEFAULT(CICompilerCountPerCPU) && CICompilerCountPerCPU) { + warning("The VM option CICompilerCountPerCPU overrides CICompilerCount."); + } +#endif + + if (BackgroundCompilation && (CompileTheWorld || ReplayCompiles)) { + if (!FLAG_IS_DEFAULT(BackgroundCompilation)) { + warning("BackgroundCompilation disabled due to CompileTheWorld or ReplayCompiles options."); + } + FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); + } + +#ifdef COMPILER2 + if (PostLoopMultiversioning && !RangeCheckElimination) { + if (!FLAG_IS_DEFAULT(PostLoopMultiversioning)) { + warning("PostLoopMultiversioning disabled because RangeCheckElimination is disabled."); + } + 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; + } +#endif // COMPILER2 + + if (Arguments::is_interpreter_only()) { + if (UseCompiler) { + if (!FLAG_IS_DEFAULT(UseCompiler)) { + warning("UseCompiler disabled due to -Xint."); + } + FLAG_SET_CMDLINE(bool, UseCompiler, false); + } + if (ProfileInterpreter) { + if (!FLAG_IS_DEFAULT(ProfileInterpreter)) { + warning("ProfileInterpreter disabled due to -Xint."); + } + FLAG_SET_CMDLINE(bool, ProfileInterpreter, false); + } + if (TieredCompilation) { + if (!FLAG_IS_DEFAULT(TieredCompilation)) { + warning("TieredCompilation disabled due to -Xint."); + } + FLAG_SET_CMDLINE(bool, TieredCompilation, false); + } +#if INCLUDE_JVMCI + if (EnableJVMCI) { + if (!FLAG_IS_DEFAULT(EnableJVMCI) || !FLAG_IS_DEFAULT(UseJVMCICompiler)) { + warning("JVMCI Compiler disabled due to -Xint."); + } + FLAG_SET_CMDLINE(bool, EnableJVMCI, false); + FLAG_SET_CMDLINE(bool, UseJVMCICompiler, false); + } +#endif + } else { +#if INCLUDE_JVMCI + status = status && JVMCIGlobals::check_jvmci_flags_are_consistent(); +#endif + } + return status; +} + +void CompilerConfig::ergo_initialize() { + if (Arguments::is_interpreter_only()) { + return; // Nothing to do. + } + +#ifdef TIERED + if (!compilation_mode_selected()) { + select_compilation_mode_ergonomically(); + } +#endif + +#if INCLUDE_JVMCI + // Check that JVMCI compiler supports selested GC. + // Should be done after GCConfig::initialize() was called. + JVMCIGlobals::check_jvmci_supported_gc(); + set_jvmci_specific_flags(); +#endif + + if (TieredCompilation) { + set_tiered_flags(); + } else { + int max_compilation_policy_choice = 1; +#ifdef COMPILER2 + if (is_server_compilation_mode_vm()) { + max_compilation_policy_choice = 2; + } +#endif + // Check if the policy is valid. + if (CompilationPolicyChoice >= max_compilation_policy_choice) { + vm_exit_during_initialization( + "Incompatible compilation policy selected", NULL); + } + // Scale CompileThreshold + // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves CompileThreshold unchanged. + if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { + FLAG_SET_ERGO(intx, CompileThreshold, scaled_compile_threshold(CompileThreshold)); + } + } + + if (UseOnStackReplacement && !UseLoopCounter) { + warning("On-stack-replacement requires loop counters; enabling loop counters"); + FLAG_SET_DEFAULT(UseLoopCounter, true); + } + +#ifdef COMPILER2 + if (!EliminateLocks) { + EliminateNestedLocks = false; + } + if (!Inline) { + IncrementalInline = false; + } +#ifndef PRODUCT + if (!IncrementalInline) { + AlwaysIncrementalInline = false; + } + if (PrintIdealGraphLevel > 0) { + FLAG_SET_ERGO(bool, PrintIdealGraph, true); + } +#endif + if (!UseTypeSpeculation && FLAG_IS_DEFAULT(TypeProfileLevel)) { + // nothing to use the profiling, turn if off + FLAG_SET_DEFAULT(TypeProfileLevel, 0); + } + if (!FLAG_IS_DEFAULT(OptoLoopAlignment) && FLAG_IS_DEFAULT(MaxLoopPad)) { + FLAG_SET_DEFAULT(MaxLoopPad, OptoLoopAlignment-1); + } + if (FLAG_IS_DEFAULT(LoopStripMiningIterShortLoop)) { + // blind guess + LoopStripMiningIterShortLoop = LoopStripMiningIter / 10; + } +#endif // COMPILER2 +} diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index 16ae7b0e3af..6529d2f29d9 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,7 +25,7 @@ #ifndef SHARE_VM_COMPILER_COMPILERDEFINITIONS_HPP #define SHARE_VM_COMPILER_COMPILERDEFINITIONS_HPP -#include "utilities/globalDefinitions.hpp" +#include "memory/allocation.hpp" // The (closed set) of concrete compiler classes. enum CompilerType { @@ -75,8 +75,6 @@ inline bool is_client_compilation_mode_vm() { return Compilation_mode == CompMode_client; } -extern void set_client_compilation_mode(); - inline bool is_c1_compile(int comp_level) { return comp_level > CompLevel_none && comp_level < CompLevel_full_optimization; } @@ -109,4 +107,23 @@ enum RTMState { #define RTM_OPT_ONLY(code) #endif +class CompilerConfig : public AllStatic { +public: + // Scale compile thresholds + // Returns threshold scaled with CompileThresholdScaling + static intx scaled_compile_threshold(intx threshold, double scale); + static intx scaled_compile_threshold(intx threshold); + + // Returns freq_log scaled with CompileThresholdScaling + static intx scaled_freq_log(intx freq_log, double scale); + static intx scaled_freq_log(intx freq_log); + + static bool check_args_consistency(bool status); + + static void ergo_initialize(); + +private: + static void set_tiered_flags(); +}; + #endif // SHARE_VM_COMPILER_COMPILERDEFINITIONS_HPP diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 2dba9098e9e..7a789226041 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -50,7 +50,7 @@ class G1BarrierSet: public CardTableBarrierSet { // pre-marking object graph. static void enqueue(oop pre_val); - static void enqueue_if_weak_or_archive(DecoratorSet decorators, oop value); + static void enqueue_if_weak(DecoratorSet decorators, oop value); template void write_ref_array_pre_work(T* dst, size_t count); virtual void write_ref_array_pre(oop* dst, size_t count, bool dest_uninitialized); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 759684da212..f16f2e56cd8 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp @@ -54,15 +54,14 @@ inline void G1BarrierSet::write_ref_field_post(T* field, oop new_val) { } } -inline void G1BarrierSet::enqueue_if_weak_or_archive(DecoratorSet decorators, oop value) { +inline void G1BarrierSet::enqueue_if_weak(DecoratorSet decorators, oop value) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); - // Archive roots need to be enqueued since they add subgraphs to the - // Java heap that were not there at the snapshot when marking started. - // Weak and phantom references also need enqueueing for similar reasons. - const bool in_archive_root = (decorators & IN_ARCHIVE_ROOT) != 0; + // Loading from a weak or phantom reference needs enqueueing, as + // the object may not have been reachable (part of the snapshot) + // when marking started. const bool on_strong_oop_ref = (decorators & ON_STRONG_OOP_REF) != 0; const bool peek = (decorators & AS_NO_KEEPALIVE) != 0; - const bool needs_enqueue = in_archive_root || (!peek && !on_strong_oop_ref); + const bool needs_enqueue = (!peek && !on_strong_oop_ref); if (needs_enqueue && value != NULL) { enqueue(value); @@ -74,7 +73,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_not_in_heap(T* addr) { oop value = ModRef::oop_load_not_in_heap(addr); - enqueue_if_weak_or_archive(decorators, value); + enqueue_if_weak(decorators, value); return value; } @@ -83,7 +82,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_in_heap(T* addr) { oop value = ModRef::oop_load_in_heap(addr); - enqueue_if_weak_or_archive(decorators, value); + enqueue_if_weak(decorators, value); return value; } @@ -91,7 +90,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_in_heap_at(oop base, ptrdiff_t offset) { oop value = ModRef::oop_load_in_heap_at(base, offset); - enqueue_if_weak_or_archive(AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset), value); + enqueue_if_weak(AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset), value); return value; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 48cbd9802e2..6c998612192 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -78,6 +78,7 @@ #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/iterator.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" @@ -823,6 +824,18 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { decrease_used(size_used); } +oop G1CollectedHeap::materialize_archived_object(oop obj) { + assert(obj != NULL, "archived obj is NULL"); + assert(MetaspaceShared::is_archive_object(obj), "must be archived object"); + + // Loading an archived object makes it strongly reachable. If it is + // loaded during concurrent marking, it must be enqueued to the SATB + // queue, shading the previously white object gray. + G1BarrierSet::enqueue(obj); + + return obj; +} + HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { ResourceMark rm; // For retrieving the thread names in log messages. @@ -3249,6 +3262,9 @@ public: if (process_symbols) { SymbolTable::clear_parallel_claimed_index(); } + if (process_strings) { + StringTable::reset_dead_counter(); + } } ~G1StringAndSymbolCleaningTask() { @@ -3262,6 +3278,9 @@ public: "symbols: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed", strings_processed(), strings_removed(), symbols_processed(), symbols_removed()); + if (_process_strings) { + StringTable::finish_dead_counter(); + } } void work(uint worker_id) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 2d9c042bced..31cf2d99764 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -699,6 +699,8 @@ public: // mapping failed, with the same non-overlapping and sorted MemRegion array. void dealloc_archive_regions(MemRegion* range, size_t count); + oop materialize_archived_object(oop obj); + private: // Shrink the garbage-first heap by at most the given size (in bytes!). diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 9e934be48ab..6740b874caf 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -315,6 +315,20 @@ public: guarantee(!r->is_young() || r->rem_set()->is_complete(), "Remembered set for Young region %u must be complete, is %s", r->hrm_index(), r->rem_set()->get_state_str()); // Humongous and old regions regions might be of any state, so can't check here. guarantee(!r->is_free() || !r->rem_set()->is_tracked(), "Remembered set for free region %u must be untracked, is %s", r->hrm_index(), r->rem_set()->get_state_str()); + // Verify that the continues humongous regions' remembered set state matches the + // one from the starts humongous region. + if (r->is_continues_humongous()) { + if (r->rem_set()->get_state_str() != r->humongous_start_region()->rem_set()->get_state_str()) { + log_error(gc, verify)("Remset states differ: Region %u (%s) remset %s with starts region %u (%s) remset %s", + r->hrm_index(), + r->get_short_type_str(), + r->rem_set()->get_state_str(), + r->humongous_start_region()->hrm_index(), + r->humongous_start_region()->get_short_type_str(), + r->humongous_start_region()->rem_set()->get_state_str()); + _failures = true; + } + } // For archive regions, verify there are no heap pointers to // non-pinned regions. For all others, verify liveness info. if (r->is_closed_archive()) { diff --git a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp index 7b24629c69f..5c92fcc6ac1 100644 --- a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp @@ -30,7 +30,7 @@ #include "runtime/safepoint.hpp" bool G1RemSetTrackingPolicy::is_interesting_humongous_region(HeapRegion* r) const { - return r->is_starts_humongous() && oop(r->bottom())->is_typeArray(); + return r->is_humongous() && oop(r->humongous_start_region()->bottom())->is_typeArray(); } bool G1RemSetTrackingPolicy::needs_scan_for_rebuild(HeapRegion* r) const { @@ -119,13 +119,21 @@ void G1RemSetTrackingPolicy::update_after_rebuild(HeapRegion* r) { if (r->rem_set()->is_updating()) { r->rem_set()->set_state_complete(); } + G1CollectedHeap* g1h = G1CollectedHeap::heap(); // We can drop remembered sets of humongous regions that have a too large remembered set: // We will never try to eagerly reclaim or move them anyway until the next concurrent // cycle as e.g. remembered set entries will always be added. - if (r->is_humongous() && !G1CollectedHeap::heap()->is_potential_eager_reclaim_candidate(r)) { - r->rem_set()->clear_locked(true /* only_cardset */); + if (r->is_starts_humongous() && !g1h->is_potential_eager_reclaim_candidate(r)) { + // Handle HC regions with the HS region. + uint const size_in_regions = (uint)g1h->humongous_obj_size_in_regions(oop(r->bottom())->size()); + uint const region_idx = r->hrm_index(); + for (uint j = region_idx; j < (region_idx + size_in_regions); j++) { + HeapRegion* const cur = g1h->region_at(j); + assert(!cur->is_continues_humongous() || cur->rem_set()->is_empty(), + "Continues humongous region %u remset should be empty", j); + cur->rem_set()->clear_locked(true /* only_cardset */); + } } - assert(!r->is_continues_humongous() || r->rem_set()->is_empty(), "Continues humongous object remsets should be empty"); G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); log_trace(gc, remset, tracking)("After rebuild region %u " "(ntams " PTR_FORMAT " " diff --git a/src/hotspot/share/gc/g1/g1StringDedup.cpp b/src/hotspot/share/gc/g1/g1StringDedup.cpp index 58d0e2d0309..5656a83e27f 100644 --- a/src/hotspot/share/gc/g1/g1StringDedup.cpp +++ b/src/hotspot/share/gc/g1/g1StringDedup.cpp @@ -29,26 +29,16 @@ #include "gc/g1/g1StringDedup.hpp" #include "gc/g1/g1StringDedupQueue.hpp" #include "gc/g1/g1StringDedupStat.hpp" -#include "gc/g1/g1StringDedupTable.hpp" -#include "gc/g1/g1StringDedupThread.hpp" +#include "gc/shared/stringdedup/stringDedup.inline.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +#include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "gc/shared/stringdedup/stringDedupThread.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" -bool G1StringDedup::_enabled = false; - void G1StringDedup::initialize() { - assert(UseG1GC, "String deduplication only available with G1"); - if (UseStringDeduplication) { - _enabled = true; - G1StringDedupQueue::create(); - G1StringDedupTable::create(); - G1StringDedupThread::create(); - } -} - -void G1StringDedup::stop() { - assert(is_enabled(), "String deduplication not enabled"); - G1StringDedupThread::thread()->stop(); + assert(UseG1GC, "String deduplication available with G1"); + StringDedup::initialize_impl(); } bool G1StringDedup::is_candidate_from_mark(oop obj) { @@ -99,12 +89,6 @@ void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint } } -void G1StringDedup::deduplicate(oop java_string) { - assert(is_enabled(), "String deduplication not enabled"); - G1StringDedupStat dummy; // Statistics from this path is never used - G1StringDedupTable::deduplicate(java_string, dummy); -} - void G1StringDedup::oops_do(OopClosure* keep_alive) { assert(is_enabled(), "String deduplication not enabled"); unlink_or_oops_do(NULL, keep_alive, true /* allow_resize_and_rehash */); @@ -112,8 +96,8 @@ void G1StringDedup::oops_do(OopClosure* keep_alive) { void G1StringDedup::parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) { assert(is_enabled(), "String deduplication not enabled"); - G1StringDedupQueue::unlink_or_oops_do(unlink); - G1StringDedupTable::unlink_or_oops_do(unlink, worker_id); + StringDedupQueue::unlink_or_oops_do(unlink); + StringDedupTable::unlink_or_oops_do(unlink, worker_id); } // @@ -136,11 +120,11 @@ public: virtual void work(uint worker_id) { { G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupQueueFixup, worker_id); - G1StringDedupQueue::unlink_or_oops_do(&_cl); + StringDedupQueue::unlink_or_oops_do(&_cl); } { G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupTableFixup, worker_id); - G1StringDedupTable::unlink_or_oops_do(&_cl, worker_id); + StringDedupTable::unlink_or_oops_do(&_cl, worker_id); } } }; @@ -155,61 +139,3 @@ void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive, G1CollectedHeap* g1h = G1CollectedHeap::heap(); g1h->workers()->run_task(&task); } - -void G1StringDedup::threads_do(ThreadClosure* tc) { - assert(is_enabled(), "String deduplication not enabled"); - tc->do_thread(G1StringDedupThread::thread()); -} - -void G1StringDedup::print_worker_threads_on(outputStream* st) { - assert(is_enabled(), "String deduplication not enabled"); - G1StringDedupThread::thread()->print_on(st); - st->cr(); -} - -void G1StringDedup::verify() { - assert(is_enabled(), "String deduplication not enabled"); - G1StringDedupQueue::verify(); - G1StringDedupTable::verify(); -} - -G1StringDedupUnlinkOrOopsDoClosure::G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, - OopClosure* keep_alive, - bool allow_resize_and_rehash) : - _is_alive(is_alive), - _keep_alive(keep_alive), - _resized_table(NULL), - _rehashed_table(NULL), - _next_queue(0), - _next_bucket(0) { - if (allow_resize_and_rehash) { - // If both resize and rehash is needed, only do resize. Rehash of - // the table will eventually happen if the situation persists. - _resized_table = G1StringDedupTable::prepare_resize(); - if (!is_resizing()) { - _rehashed_table = G1StringDedupTable::prepare_rehash(); - } - } -} - -G1StringDedupUnlinkOrOopsDoClosure::~G1StringDedupUnlinkOrOopsDoClosure() { - assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash"); - if (is_resizing()) { - G1StringDedupTable::finish_resize(_resized_table); - } else if (is_rehashing()) { - G1StringDedupTable::finish_rehash(_rehashed_table); - } -} - -// Atomically claims the next available queue for exclusive access by -// the current thread. Returns the queue number of the claimed queue. -size_t G1StringDedupUnlinkOrOopsDoClosure::claim_queue() { - return Atomic::add((size_t)1, &_next_queue) - 1; -} - -// Atomically claims the next available table partition for exclusive -// access by the current thread. Returns the table bucket number where -// the claimed partition starts. -size_t G1StringDedupUnlinkOrOopsDoClosure::claim_table_partition(size_t partition_size) { - return Atomic::add(partition_size, &_next_bucket) - partition_size; -} diff --git a/src/hotspot/share/gc/g1/g1StringDedup.hpp b/src/hotspot/share/gc/g1/g1StringDedup.hpp index 2e6a81d9d4e..f7b4d12ac23 100644 --- a/src/hotspot/share/gc/g1/g1StringDedup.hpp +++ b/src/hotspot/share/gc/g1/g1StringDedup.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -26,30 +26,7 @@ #define SHARE_VM_GC_G1_G1STRINGDEDUP_HPP // -// String Deduplication -// -// String deduplication aims to reduce the heap live-set by deduplicating identical -// instances of String so that they share the same backing character array. -// -// The deduplication process is divided in two main parts, 1) finding the objects to -// deduplicate, and 2) deduplicating those objects. The first part is done as part of -// a normal GC cycle when objects are marked or evacuated. At this time a check is -// applied on each object to check if it is a candidate for deduplication. If so, the -// object is placed on the deduplication queue for later processing. The second part, -// processing the objects on the deduplication queue, is a concurrent phase which -// starts right after the stop-the-wold marking/evacuation phase. This phase is -// executed by the deduplication thread, which pulls deduplication candidates of the -// deduplication queue and tries to deduplicate them. -// -// A deduplication hashtable is used to keep track of all unique character arrays -// used by String objects. When deduplicating, a lookup is made in this table to see -// if there is already an identical character array somewhere on the heap. If so, the -// String object is adjusted to point to that character array, releasing the reference -// to the original array allowing it to eventually be garbage collected. If the lookup -// fails the character array is instead inserted into the hashtable so that this array -// can be shared at some point in the future. -// -// Candidate selection +// G1 string deduplication candidate selection // // An object is considered a deduplication candidate if all of the following // statements are true: @@ -70,36 +47,21 @@ // than the deduplication age threshold, is will never become a candidate again. // This approach avoids making the same object a candidate more than once. // -// Interned strings are a bit special. They are explicitly deduplicated just before -// being inserted into the StringTable (to avoid counteracting C2 optimizations done -// on string literals), then they also become deduplication candidates if they reach -// the deduplication age threshold or are evacuated to an old heap region. The second -// attempt to deduplicate such strings will be in vain, but we have no fast way of -// filtering them out. This has not shown to be a problem, as the number of interned -// strings is usually dwarfed by the number of normal (non-interned) strings. -// -// For additional information on string deduplication, please see JEP 192, -// http://openjdk.java.net/jeps/192 -// +#include "gc/shared/stringdedup/stringDedup.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" class OopClosure; class BoolObjectClosure; -class ThreadClosure; -class outputStream; -class G1StringDedupTable; -class G1StringDedupUnlinkOrOopsDoClosure; class G1GCPhaseTimes; +class G1StringDedupUnlinkOrOopsDoClosure; // -// Main interface for interacting with string deduplication. +// G1 interface for interacting with string deduplication. // -class G1StringDedup : public AllStatic { +class G1StringDedup : public StringDedup { private: - // Single state for checking if both G1 and string deduplication is enabled. - static bool _enabled; // Candidate selection policies, returns true if the given object is // candidate for string deduplication. @@ -107,21 +69,9 @@ private: static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj); public: - // Returns true if both G1 and string deduplication is enabled. - static bool is_enabled() { - return _enabled; - } - // Initialize string deduplication. static void initialize(); - // Stop the deduplication thread. - static void stop(); - - // Immediately deduplicates the given String object, bypassing the - // the deduplication queue. - static void deduplicate(oop java_string); - // Enqueues a deduplication candidate for later processing by the deduplication // thread. Before enqueuing, these functions apply the appropriate candidate // selection policy to filters out non-candidates. @@ -133,70 +83,28 @@ public: static void parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id); static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool allow_resize_and_rehash, G1GCPhaseTimes* phase_times = NULL); - - static void threads_do(ThreadClosure* tc); - static void print_worker_threads_on(outputStream* st); - static void verify(); }; // // This closure encapsulates the state and the closures needed when scanning // the deduplication queue and table during the unlink_or_oops_do() operation. // A single instance of this closure is created and then shared by all worker -// threads participating in the scan. The _next_queue and _next_bucket fields -// provide a simple mechanism for GC workers to claim exclusive access to a -// queue or a table partition. +// threads participating in the scan. // -class G1StringDedupUnlinkOrOopsDoClosure : public StackObj { -private: - BoolObjectClosure* _is_alive; - OopClosure* _keep_alive; - G1StringDedupTable* _resized_table; - G1StringDedupTable* _rehashed_table; - size_t _next_queue; - size_t _next_bucket; - +class G1StringDedupUnlinkOrOopsDoClosure : public StringDedupUnlinkOrOopsDoClosure { public: G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, OopClosure* keep_alive, - bool allow_resize_and_rehash); - ~G1StringDedupUnlinkOrOopsDoClosure(); - - bool is_resizing() { - return _resized_table != NULL; - } - - G1StringDedupTable* resized_table() { - return _resized_table; - } - - bool is_rehashing() { - return _rehashed_table != NULL; - } - - // Atomically claims the next available queue for exclusive access by - // the current thread. Returns the queue number of the claimed queue. - size_t claim_queue(); - - // Atomically claims the next available table partition for exclusive - // access by the current thread. Returns the table bucket number where - // the claimed partition starts. - size_t claim_table_partition(size_t partition_size); - - // Applies and returns the result from the is_alive closure, or - // returns true if no such closure was provided. - bool is_alive(oop o) { - if (_is_alive != NULL) { - return _is_alive->do_object_b(o); + bool allow_resize_and_rehash) : + StringDedupUnlinkOrOopsDoClosure(is_alive, keep_alive) { + if (G1StringDedup::is_enabled()) { + G1StringDedup::gc_prologue(allow_resize_and_rehash); + } } - return true; - } - // Applies the keep_alive closure, or does nothing if no such - // closure was provided. - void keep_alive(oop* p) { - if (_keep_alive != NULL) { - _keep_alive->do_oop(p); + ~G1StringDedupUnlinkOrOopsDoClosure() { + if (G1StringDedup::is_enabled()) { + G1StringDedup::gc_epilogue(); } } }; diff --git a/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp b/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp index b4116d1df24..e1b22f9d7e9 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp +++ b/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -34,7 +34,6 @@ #include "runtime/safepointVerifiers.hpp" #include "utilities/stack.inline.hpp" -G1StringDedupQueue* G1StringDedupQueue::_queue = NULL; const size_t G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue const size_t G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue @@ -54,54 +53,49 @@ G1StringDedupQueue::~G1StringDedupQueue() { ShouldNotReachHere(); } -void G1StringDedupQueue::create() { - assert(_queue == NULL, "One string deduplication queue allowed"); - _queue = new G1StringDedupQueue(); -} - -void G1StringDedupQueue::wait() { +void G1StringDedupQueue::wait_impl() { MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); - while (_queue->_empty && !_queue->_cancel) { + while (_empty && !_cancel) { ml.wait(Mutex::_no_safepoint_check_flag); } } -void G1StringDedupQueue::cancel_wait() { +void G1StringDedupQueue::cancel_wait_impl() { MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); - _queue->_cancel = true; + _cancel = true; ml.notify(); } -void G1StringDedupQueue::push(uint worker_id, oop java_string) { +void G1StringDedupQueue::push_impl(uint worker_id, oop java_string) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); - assert(worker_id < _queue->_nqueues, "Invalid queue"); + assert(worker_id < _nqueues, "Invalid queue"); // Push and notify waiter - G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id]; + G1StringDedupWorkerQueue& worker_queue = _queues[worker_id]; if (!worker_queue.is_full()) { worker_queue.push(java_string); - if (_queue->_empty) { + if (_empty) { MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); - if (_queue->_empty) { + if (_empty) { // Mark non-empty and notify waiter - _queue->_empty = false; + _empty = false; ml.notify(); } } } else { // Queue is full, drop the string and update the statistics - Atomic::inc(&_queue->_dropped); + Atomic::inc(&_dropped); } } -oop G1StringDedupQueue::pop() { +oop G1StringDedupQueue::pop_impl() { assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint"); NoSafepointVerifier nsv; // Try all queues before giving up - for (size_t tries = 0; tries < _queue->_nqueues; tries++) { + for (size_t tries = 0; tries < _nqueues; tries++) { // The cursor indicates where we left of last time - G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor]; + G1StringDedupWorkerQueue* queue = &_queues[_cursor]; while (!queue->is_empty()) { oop obj = queue->pop(); // The oop we pop can be NULL if it was marked @@ -112,34 +106,18 @@ oop G1StringDedupQueue::pop() { } // Try next queue - _queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues; + _cursor = (_cursor + 1) % _nqueues; } // Mark empty - _queue->_empty = true; + _empty = true; return NULL; } -void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) { - // A worker thread first claims a queue, which ensures exclusive - // access to that queue, then continues to process it. - for (;;) { - // Grab next queue to scan - size_t queue = cl->claim_queue(); - if (queue >= _queue->_nqueues) { - // End of queues - break; - } - - // Scan the queue - unlink_or_oops_do(cl, queue); - } -} - -void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { - assert(queue < _queue->_nqueues, "Invalid queue"); - StackIterator iter(_queue->_queues[queue]); +void G1StringDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { + assert(queue < _nqueues, "Invalid queue"); + StackIterator iter(_queues[queue]); while (!iter.is_empty()) { oop* p = iter.next_addr(); if (*p != NULL) { @@ -153,14 +131,14 @@ void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c } } -void G1StringDedupQueue::print_statistics() { +void G1StringDedupQueue::print_statistics_impl() { log_debug(gc, stringdedup)(" Queue"); - log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _queue->_dropped); + log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _dropped); } -void G1StringDedupQueue::verify() { - for (size_t i = 0; i < _queue->_nqueues; i++) { - StackIterator iter(_queue->_queues[i]); +void G1StringDedupQueue::verify_impl() { + for (size_t i = 0; i < _nqueues; i++) { + StackIterator iter(_queues[i]); while (!iter.is_empty()) { oop obj = iter.next(); if (obj != NULL) { diff --git a/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp b/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp index 6bc37c2679c..20e13fc4494 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp +++ b/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -25,40 +25,21 @@ #ifndef SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP #define SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP +#include "gc/shared/stringdedup/stringDedupQueue.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" #include "utilities/stack.hpp" -class G1StringDedupUnlinkOrOopsDoClosure; +class StringDedupUnlinkOrOopsDoClosure; // -// The deduplication queue acts as the communication channel between the stop-the-world -// mark/evacuation phase and the concurrent deduplication phase. Deduplication candidates -// found during mark/evacuation are placed on this queue for later processing in the -// deduplication thread. A queue entry is an oop pointing to a String object (as opposed -// to entries in the deduplication hashtable which points to character arrays). +// G1 enqueues candidates during the stop-the-world mark/evacuation phase. // -// While users of the queue treat it as a single queue, it is implemented as a set of -// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue -// operations by the GC workers. -// -// The oops in the queue are treated as weak pointers, meaning the objects they point to -// can become unreachable and pruned (cleared) before being popped by the deduplication -// thread. -// -// Pushing to the queue is thread safe (this relies on each thread using a unique worker -// id), but only allowed during a safepoint. Popping from the queue is NOT thread safe -// and can only be done by the deduplication thread outside a safepoint. -// -// The StringDedupQueue_lock is only used for blocking and waking up the deduplication -// thread in case the queue is empty or becomes non-empty, respectively. This lock does -// not otherwise protect the queue content. -// -class G1StringDedupQueue : public CHeapObj { + +class G1StringDedupQueue : public StringDedupQueue { private: typedef Stack G1StringDedupWorkerQueue; - static G1StringDedupQueue* _queue; static const size_t _max_size; static const size_t _max_cache_size; @@ -71,31 +52,36 @@ private: // Statistics counter, only used for logging. uintx _dropped; - G1StringDedupQueue(); ~G1StringDedupQueue(); - static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue); + void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue); public: - static void create(); + G1StringDedupQueue(); + +protected: // Blocks and waits for the queue to become non-empty. - static void wait(); + void wait_impl(); // Wakes up any thread blocked waiting for the queue to become non-empty. - static void cancel_wait(); + void cancel_wait_impl(); // Pushes a deduplication candidate onto a specific GC worker queue. - static void push(uint worker_id, oop java_string); + void push_impl(uint worker_id, oop java_string); // Pops a deduplication candidate from any queue, returns NULL if // all queues are empty. - static oop pop(); + oop pop_impl(); - static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl); + size_t num_queues() const { + return _nqueues; + } - static void print_statistics(); - static void verify(); + void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue); + + void print_statistics_impl(); + void verify_impl(); }; #endif // SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP diff --git a/src/hotspot/share/gc/g1/g1StringDedupStat.cpp b/src/hotspot/share/gc/g1/g1StringDedupStat.cpp index 7c1a60ad5e1..0a2c30f7833 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupStat.cpp +++ b/src/hotspot/share/gc/g1/g1StringDedupStat.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -23,121 +23,60 @@ */ #include "precompiled.hpp" + +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1StringDedupStat.hpp" #include "logging/log.hpp" -G1StringDedupStat::G1StringDedupStat() : - _inspected(0), - _skipped(0), - _hashed(0), - _known(0), - _new(0), - _new_bytes(0), - _deduped(0), - _deduped_bytes(0), +G1StringDedupStat::G1StringDedupStat() : StringDedupStat(), _deduped_young(0), _deduped_young_bytes(0), _deduped_old(0), _deduped_old_bytes(0), - _idle(0), - _exec(0), - _block(0), - _start_concurrent(0.0), - _end_concurrent(0.0), - _start_phase(0.0), - _idle_elapsed(0.0), - _exec_elapsed(0.0), - _block_elapsed(0.0) { + _heap(G1CollectedHeap::heap()) { } -void G1StringDedupStat::add(const G1StringDedupStat& stat) { - _inspected += stat._inspected; - _skipped += stat._skipped; - _hashed += stat._hashed; - _known += stat._known; - _new += stat._new; - _new_bytes += stat._new_bytes; - _deduped += stat._deduped; - _deduped_bytes += stat._deduped_bytes; - _deduped_young += stat._deduped_young; - _deduped_young_bytes += stat._deduped_young_bytes; - _deduped_old += stat._deduped_old; - _deduped_old_bytes += stat._deduped_old_bytes; - _idle += stat._idle; - _exec += stat._exec; - _block += stat._block; - _idle_elapsed += stat._idle_elapsed; - _exec_elapsed += stat._exec_elapsed; - _block_elapsed += stat._block_elapsed; -} -void G1StringDedupStat::print_start(const G1StringDedupStat& last_stat) { - log_info(gc, stringdedup)( - "Concurrent String Deduplication (" G1_STRDEDUP_TIME_FORMAT ")", - G1_STRDEDUP_TIME_PARAM(last_stat._start_concurrent)); -} -void G1StringDedupStat::print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) { - double total_deduped_bytes_percent = 0.0; - - if (total_stat._new_bytes > 0) { - // Avoid division by zero - total_deduped_bytes_percent = percent_of(total_stat._deduped_bytes, total_stat._new_bytes); - } - - log_info(gc, stringdedup)( - "Concurrent String Deduplication " - G1_STRDEDUP_BYTES_FORMAT_NS "->" G1_STRDEDUP_BYTES_FORMAT_NS "(" G1_STRDEDUP_BYTES_FORMAT_NS ") " - "avg " G1_STRDEDUP_PERCENT_FORMAT_NS " " - "(" G1_STRDEDUP_TIME_FORMAT ", " G1_STRDEDUP_TIME_FORMAT ") " G1_STRDEDUP_TIME_FORMAT_MS, - G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes), - G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes - last_stat._deduped_bytes), - G1_STRDEDUP_BYTES_PARAM(last_stat._deduped_bytes), - total_deduped_bytes_percent, - G1_STRDEDUP_TIME_PARAM(last_stat._start_concurrent), - G1_STRDEDUP_TIME_PARAM(last_stat._end_concurrent), - G1_STRDEDUP_TIME_PARAM_MS(last_stat._exec_elapsed)); -} - -void G1StringDedupStat::print_statistics(const G1StringDedupStat& stat, bool total) { - double skipped_percent = percent_of(stat._skipped, stat._inspected); - double hashed_percent = percent_of(stat._hashed, stat._inspected); - double known_percent = percent_of(stat._known, stat._inspected); - double new_percent = percent_of(stat._new, stat._inspected); - double deduped_percent = percent_of(stat._deduped, stat._new); - double deduped_bytes_percent = percent_of(stat._deduped_bytes, stat._new_bytes); - double deduped_young_percent = percent_of(stat._deduped_young, stat._deduped); - double deduped_young_bytes_percent = percent_of(stat._deduped_young_bytes, stat._deduped_bytes); - double deduped_old_percent = percent_of(stat._deduped_old, stat._deduped); - double deduped_old_bytes_percent = percent_of(stat._deduped_old_bytes, stat._deduped_bytes); - - if (total) { - log_debug(gc, stringdedup)( - " Total Exec: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS - ", Idle: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS - ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS, - stat._exec, G1_STRDEDUP_TIME_PARAM_MS(stat._exec_elapsed), - stat._idle, G1_STRDEDUP_TIME_PARAM_MS(stat._idle_elapsed), - stat._block, G1_STRDEDUP_TIME_PARAM_MS(stat._block_elapsed)); +void G1StringDedupStat::deduped(oop obj, uintx bytes) { + StringDedupStat::deduped(obj, bytes); + if (_heap->is_in_young(obj)) { + _deduped_young ++; + _deduped_young_bytes += bytes; } else { - log_debug(gc, stringdedup)( - " Last Exec: " G1_STRDEDUP_TIME_FORMAT_MS - ", Idle: " G1_STRDEDUP_TIME_FORMAT_MS - ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS, - G1_STRDEDUP_TIME_PARAM_MS(stat._exec_elapsed), - G1_STRDEDUP_TIME_PARAM_MS(stat._idle_elapsed), - stat._block, G1_STRDEDUP_TIME_PARAM_MS(stat._block_elapsed)); + _deduped_old ++; + _deduped_old_bytes += bytes; } - log_debug(gc, stringdedup)(" Inspected: " G1_STRDEDUP_OBJECTS_FORMAT, stat._inspected); - log_debug(gc, stringdedup)(" Skipped: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._skipped, skipped_percent); - log_debug(gc, stringdedup)(" Hashed: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._hashed, hashed_percent); - log_debug(gc, stringdedup)(" Known: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._known, known_percent); - log_debug(gc, stringdedup)(" New: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT, - stat._new, new_percent, G1_STRDEDUP_BYTES_PARAM(stat._new_bytes)); - log_debug(gc, stringdedup)(" Deduplicated: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", - stat._deduped, deduped_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_bytes), deduped_bytes_percent); - log_debug(gc, stringdedup)(" Young: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", - stat._deduped_young, deduped_young_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_young_bytes), deduped_young_bytes_percent); - log_debug(gc, stringdedup)(" Old: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", - stat._deduped_old, deduped_old_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_old_bytes), deduped_old_bytes_percent); +} + +void G1StringDedupStat::add(const StringDedupStat* const stat) { + StringDedupStat::add(stat); + const G1StringDedupStat* const g1_stat = (const G1StringDedupStat* const)stat; + _deduped_young += g1_stat->_deduped_young; + _deduped_young_bytes += g1_stat->_deduped_young_bytes; + _deduped_old += g1_stat->_deduped_old; + _deduped_old_bytes += g1_stat->_deduped_old_bytes; +} + +void G1StringDedupStat::print_statistics(bool total) const { + StringDedupStat::print_statistics(total); + + double deduped_young_percent = percent_of(_deduped_young, _deduped); + double deduped_young_bytes_percent = percent_of(_deduped_young_bytes, _deduped_bytes); + double deduped_old_percent = percent_of(_deduped_old, _deduped); + double deduped_old_bytes_percent = percent_of(_deduped_old_bytes, _deduped_bytes); + + log_debug(gc, stringdedup)(" Young: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", + _deduped_young, deduped_young_percent, STRDEDUP_BYTES_PARAM(_deduped_young_bytes), deduped_young_bytes_percent); + log_debug(gc, stringdedup)(" Old: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", + _deduped_old, deduped_old_percent, STRDEDUP_BYTES_PARAM(_deduped_old_bytes), deduped_old_bytes_percent); + +} + +void G1StringDedupStat::reset() { + StringDedupStat::reset(); + _deduped_young = 0; + _deduped_young_bytes = 0; + _deduped_old = 0; + _deduped_old_bytes = 0; } diff --git a/src/hotspot/share/gc/g1/g1StringDedupStat.hpp b/src/hotspot/share/gc/g1/g1StringDedupStat.hpp index d83e627fdc4..0b30beeb95b 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupStat.hpp +++ b/src/hotspot/share/gc/g1/g1StringDedupStat.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,126 +25,28 @@ #ifndef SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP #define SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP -#include "memory/allocation.hpp" -#include "runtime/os.hpp" +#include "gc/shared/stringdedup/stringDedupStat.hpp" -// Macros for GC log output formating -#define G1_STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12) -#define G1_STRDEDUP_TIME_FORMAT "%.3fs" -#define G1_STRDEDUP_TIME_PARAM(time) (time) -#define G1_STRDEDUP_TIME_FORMAT_MS "%.3fms" -#define G1_STRDEDUP_TIME_PARAM_MS(time) ((time) * MILLIUNITS) -#define G1_STRDEDUP_PERCENT_FORMAT "%5.1f%%" -#define G1_STRDEDUP_PERCENT_FORMAT_NS "%.1f%%" -#define G1_STRDEDUP_BYTES_FORMAT "%8.1f%s" -#define G1_STRDEDUP_BYTES_FORMAT_NS "%.1f%s" -#define G1_STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes)) - -// -// Statistics gathered by the deduplication thread. -// -class G1StringDedupStat : public StackObj { +// G1 extension for gathering/reporting generational statistics +class G1StringDedupStat : public StringDedupStat { private: - // Counters - uintx _inspected; - uintx _skipped; - uintx _hashed; - uintx _known; - uintx _new; - uintx _new_bytes; - uintx _deduped; - uintx _deduped_bytes; uintx _deduped_young; uintx _deduped_young_bytes; uintx _deduped_old; uintx _deduped_old_bytes; - uintx _idle; - uintx _exec; - uintx _block; - // Time spent by the deduplication thread in different phases - double _start_concurrent; - double _end_concurrent; - double _start_phase; - double _idle_elapsed; - double _exec_elapsed; - double _block_elapsed; + G1CollectedHeap* const _heap; public: G1StringDedupStat(); - void inc_inspected() { - _inspected++; - } + void deduped(oop obj, uintx bytes); - void inc_skipped() { - _skipped++; - } + void add(const StringDedupStat* const stat); - void inc_hashed() { - _hashed++; - } + void print_statistics(bool total) const; - void inc_known() { - _known++; - } - - void inc_new(uintx bytes) { - _new++; - _new_bytes += bytes; - } - - void inc_deduped_young(uintx bytes) { - _deduped++; - _deduped_bytes += bytes; - _deduped_young++; - _deduped_young_bytes += bytes; - } - - void inc_deduped_old(uintx bytes) { - _deduped++; - _deduped_bytes += bytes; - _deduped_old++; - _deduped_old_bytes += bytes; - } - - void mark_idle() { - _start_phase = os::elapsedTime(); - _idle++; - } - - void mark_exec() { - double now = os::elapsedTime(); - _idle_elapsed = now - _start_phase; - _start_phase = now; - _start_concurrent = now; - _exec++; - } - - void mark_block() { - double now = os::elapsedTime(); - _exec_elapsed += now - _start_phase; - _start_phase = now; - _block++; - } - - void mark_unblock() { - double now = os::elapsedTime(); - _block_elapsed += now - _start_phase; - _start_phase = now; - } - - void mark_done() { - double now = os::elapsedTime(); - _exec_elapsed += now - _start_phase; - _end_concurrent = now; - } - - void add(const G1StringDedupStat& stat); - - static void print_start(const G1StringDedupStat& last_stat); - static void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat); - static void print_statistics(const G1StringDedupStat& stat, bool total); + void reset(); }; #endif // SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP diff --git a/src/hotspot/share/gc/g1/g1StringDedupThread.cpp b/src/hotspot/share/gc/g1/g1StringDedupThread.cpp deleted file mode 100644 index 9af837b71d5..00000000000 --- a/src/hotspot/share/gc/g1/g1StringDedupThread.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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/stringTable.hpp" -#include "gc/g1/g1StringDedup.hpp" -#include "gc/g1/g1StringDedupQueue.hpp" -#include "gc/g1/g1StringDedupTable.hpp" -#include "gc/g1/g1StringDedupThread.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" -#include "logging/log.hpp" -#include "oops/access.inline.hpp" -#include "oops/oop.inline.hpp" -#include "runtime/atomic.hpp" - -G1StringDedupThread* G1StringDedupThread::_thread = NULL; - -G1StringDedupThread::G1StringDedupThread() : - ConcurrentGCThread() { - set_name("G1 StrDedup"); - create_and_start(); -} - -G1StringDedupThread::~G1StringDedupThread() { - ShouldNotReachHere(); -} - -void G1StringDedupThread::create() { - assert(G1StringDedup::is_enabled(), "String deduplication not enabled"); - assert(_thread == NULL, "One string deduplication thread allowed"); - _thread = new G1StringDedupThread(); -} - -G1StringDedupThread* G1StringDedupThread::thread() { - assert(G1StringDedup::is_enabled(), "String deduplication not enabled"); - assert(_thread != NULL, "String deduplication thread not created"); - return _thread; -} - -class G1StringDedupSharedClosure: public OopClosure { - private: - G1StringDedupStat& _stat; - - public: - G1StringDedupSharedClosure(G1StringDedupStat& stat) : _stat(stat) {} - - virtual void do_oop(oop* p) { ShouldNotReachHere(); } - virtual void do_oop(narrowOop* p) { - oop java_string = RawAccess<>::oop_load(p); - G1StringDedupTable::deduplicate(java_string, _stat); - } -}; - -// The CDS archive does not include the string dedupication table. Only the string -// table is saved in the archive. The shared strings from CDS archive need to be -// added to the string dedupication table before deduplication occurs. That is -// done in the begining of the G1StringDedupThread (see G1StringDedupThread::run() -// below). -void G1StringDedupThread::deduplicate_shared_strings(G1StringDedupStat& stat) { - G1StringDedupSharedClosure sharedStringDedup(stat); - StringTable::shared_oops_do(&sharedStringDedup); -} - -void G1StringDedupThread::run_service() { - G1StringDedupStat total_stat; - - deduplicate_shared_strings(total_stat); - - // Main loop - for (;;) { - G1StringDedupStat stat; - - stat.mark_idle(); - - // Wait for the queue to become non-empty - G1StringDedupQueue::wait(); - if (should_terminate()) { - break; - } - - { - // Include thread in safepoints - SuspendibleThreadSetJoiner sts_join; - - stat.mark_exec(); - print_start(stat); - - // Process the queue - for (;;) { - oop java_string = G1StringDedupQueue::pop(); - if (java_string == NULL) { - break; - } - - G1StringDedupTable::deduplicate(java_string, stat); - - // Safepoint this thread if needed - if (sts_join.should_yield()) { - stat.mark_block(); - sts_join.yield(); - stat.mark_unblock(); - } - } - - stat.mark_done(); - - total_stat.add(stat); - print_end(stat, total_stat); - } - - G1StringDedupTable::clean_entry_cache(); - } -} - -void G1StringDedupThread::stop_service() { - G1StringDedupQueue::cancel_wait(); -} - -void G1StringDedupThread::print_start(const G1StringDedupStat& last_stat) { - G1StringDedupStat::print_start(last_stat); -} - -void G1StringDedupThread::print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) { - G1StringDedupStat::print_end(last_stat, total_stat); - if (log_is_enabled(Debug, gc, stringdedup)) { - G1StringDedupStat::print_statistics(last_stat, false); - G1StringDedupStat::print_statistics(total_stat, true); - G1StringDedupTable::print_statistics(); - G1StringDedupQueue::print_statistics(); - } -} diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index 75af999fc0b..131e496755b 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -378,6 +378,27 @@ HeapWord* CollectedHeap::obj_allocate_raw(Klass* klass, size_t size, } HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAPS) { + HeapWord* obj = NULL; + + // In assertion mode, check that there was a sampling collector present + // in the stack. This enforces checking that no path is without a sampling + // collector. + // Only check if the sampler could actually sample something in this call path. + assert(!JvmtiExport::should_post_sampled_object_alloc() + || !JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() + || THREAD->heap_sampler().sampling_collector_present(), + "Sampling collector not present."); + + if (ThreadHeapSampler::enabled()) { + // Try to allocate the sampled object from TLAB, it is possible a sample + // point was put and the TLAB still has space. + obj = THREAD->tlab().allocate_sampled_object(size); + + if (obj != NULL) { + return obj; + } + } + ThreadLocalAllocBuffer& tlab = THREAD->tlab(); // Retain tlab and allocate object in shared space if @@ -401,7 +422,7 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAP // between minimal and new_tlab_size is accepted. size_t actual_tlab_size = 0; size_t min_tlab_size = ThreadLocalAllocBuffer::compute_min_size(size); - HeapWord* obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size); + obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size); if (obj == NULL) { assert(actual_tlab_size == 0, "Allocation failed, but actual size was updated. min: " SIZE_FORMAT ", desired: " SIZE_FORMAT ", actual: " SIZE_FORMAT, min_tlab_size, new_tlab_size, actual_tlab_size); @@ -425,6 +446,14 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAP Copy::fill_to_words(obj + hdr_size, actual_tlab_size - hdr_size, badHeapWordVal); #endif // ASSERT } + + // Send the thread information about this allocation in case a sample is + // requested. + if (ThreadHeapSampler::enabled()) { + size_t tlab_bytes_since_last_sample = THREAD->tlab().bytes_since_last_sample_point(); + THREAD->heap_sampler().check_for_sampling(obj, size, tlab_bytes_since_last_sample); + } + tlab.fill(obj, obj + size, actual_tlab_size); return obj; } @@ -526,6 +555,10 @@ void CollectedHeap::fill_with_objects(HeapWord* start, size_t words, bool zap) fill_with_object_impl(start, words, zap); } +void CollectedHeap::fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap) { + CollectedHeap::fill_with_object(start, end, zap); +} + HeapWord* CollectedHeap::allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) { diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index ecd8301624e..809d0da2a2b 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -194,6 +194,18 @@ class CollectedHeap : public CHeapObj { virtual void trace_heap(GCWhen::Type when, const GCTracer* tracer); + // Internal allocation methods. + inline static HeapWord* common_allocate_memory(Klass* klass, int size, + void (*post_setup)(Klass*, HeapWord*, int), + int size_for_post, bool init_memory, + TRAPS); + + // Internal allocation method for common obj/class/array allocations. + inline static HeapWord* allocate_memory(Klass* klass, int size, + void (*post_setup)(Klass*, HeapWord*, int), + int size_for_post, bool init_memory, + TRAPS); + // Verification functions virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size) PRODUCT_RETURN; @@ -350,6 +362,8 @@ class CollectedHeap : public CHeapObj { fill_with_object(start, pointer_delta(end, start), zap); } + virtual void fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap); + // Return the address "addr" aligned by "alignment_in_bytes" if such // an address is below "end". Return NULL otherwise. inline static HeapWord* align_allocation_or_fail(HeapWord* addr, diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp index 08c8131333f..0bca6b12d77 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp @@ -34,6 +34,7 @@ #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/handles.inline.hpp" #include "runtime/thread.inline.hpp" #include "services/lowMemoryDetector.hpp" #include "utilities/align.hpp" @@ -200,9 +201,15 @@ HeapWord* CollectedHeap::allocate_outside_tlab(Klass* klass, size_t size, NOT_PRODUCT(Universe::heap()->check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); - THREAD->incr_allocated_bytes(size * HeapWordSize); + size_t size_in_bytes = size * HeapWordSize; + THREAD->incr_allocated_bytes(size_in_bytes); + + AllocTracer::send_allocation_outside_tlab(klass, result, size_in_bytes, THREAD); + + if (ThreadHeapSampler::enabled()) { + THREAD->heap_sampler().check_for_sampling(result, size_in_bytes); + } - AllocTracer::send_allocation_outside_tlab(klass, result, size * HeapWordSize, THREAD); return result; } @@ -214,12 +221,58 @@ void CollectedHeap::init_obj(HeapWord* obj, size_t size) { Copy::fill_to_aligned_words(obj + hs, size - hs); } +HeapWord* CollectedHeap::common_allocate_memory(Klass* klass, int size, + void (*post_setup)(Klass*, HeapWord*, int), + int size_for_post, bool init_memory, + TRAPS) { + HeapWord* obj; + if (init_memory) { + obj = common_mem_allocate_init(klass, size, CHECK_NULL); + } else { + obj = common_mem_allocate_noinit(klass, size, CHECK_NULL); + } + post_setup(klass, obj, size_for_post); + return obj; +} + +HeapWord* CollectedHeap::allocate_memory(Klass* klass, int size, + void (*post_setup)(Klass*, HeapWord*, int), + int size_for_post, bool init_memory, + TRAPS) { + HeapWord* obj; + + assert(JavaThread::current()->heap_sampler().add_sampling_collector(), + "Should never return false."); + + if (JvmtiExport::should_post_sampled_object_alloc()) { + HandleMark hm(THREAD); + Handle obj_h; + { + JvmtiSampledObjectAllocEventCollector collector; + obj = common_allocate_memory(klass, size, post_setup, size_for_post, + init_memory, CHECK_NULL); + // If we want to be sampling, protect the allocated object with a Handle + // before doing the callback. The callback is done in the destructor of + // the JvmtiSampledObjectAllocEventCollector. + obj_h = Handle(THREAD, (oop) obj); + } + obj = (HeapWord*) obj_h(); + } else { + obj = common_allocate_memory(klass, size, post_setup, size_for_post, + init_memory, CHECK_NULL); + } + + assert(JavaThread::current()->heap_sampler().remove_sampling_collector(), + "Should never return false."); + return obj; +} + oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); - HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); - post_allocation_setup_obj(klass, obj, size); + HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_obj, + size, true, CHECK_NULL); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; } @@ -228,8 +281,8 @@ oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); - HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); - post_allocation_setup_class(klass, obj, size); // set oop_size + HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_class, + size, true, CHECK_NULL); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; } @@ -241,8 +294,8 @@ oop CollectedHeap::array_allocate(Klass* klass, debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); - HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); - post_allocation_setup_array(klass, obj, length); + HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array, + length, true, CHECK_NULL); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; } @@ -254,9 +307,9 @@ oop CollectedHeap::array_allocate_nozero(Klass* klass, debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); - HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL); - ((oop)obj)->set_klass_gap(0); - post_allocation_setup_array(klass, obj, length); + + HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array, + length, false, CHECK_NULL); #ifndef PRODUCT const size_t hs = oopDesc::header_size()+1; Universe::heap()->check_for_non_bad_heap_word_value(obj+hs, size-hs); diff --git a/src/hotspot/share/gc/shared/plab.cpp b/src/hotspot/share/gc/shared/plab.cpp index 92e40cf7056..ca085ad931c 100644 --- a/src/hotspot/share/gc/shared/plab.cpp +++ b/src/hotspot/share/gc/shared/plab.cpp @@ -82,14 +82,14 @@ void PLAB::retire() { size_t PLAB::retire_internal() { size_t result = 0; if (_top < _hard_end) { - CollectedHeap::fill_with_object(_top, _hard_end); + Universe::heap()->fill_with_dummy_object(_top, _hard_end, true); result += invalidate(); } return result; } void PLAB::add_undo_waste(HeapWord* obj, size_t word_sz) { - CollectedHeap::fill_with_object(obj, word_sz); + Universe::heap()->fill_with_dummy_object(obj, obj + word_sz, true); _undo_wasted += word_sz; } diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp new file mode 100644 index 00000000000..64dc3fa0b06 --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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. + * + */ +#include "precompiled.hpp" + +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +#include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" + +bool StringDedup::_enabled = false; + +void StringDedup::gc_prologue(bool resize_and_rehash_table) { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupQueue::gc_prologue(); + StringDedupTable::gc_prologue(resize_and_rehash_table); + +} +void StringDedup::gc_epilogue() { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupQueue::gc_epilogue(); + StringDedupTable::gc_epilogue(); +} + +void StringDedup::stop() { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupThread::thread()->stop(); +} + +void StringDedup::deduplicate(oop java_string) { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupStat dummy; // Statistics from this path is never used + StringDedupTable::deduplicate(java_string, &dummy); +} + + +void StringDedup::parallel_unlink(StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupQueue::unlink_or_oops_do(unlink); + StringDedupTable::unlink_or_oops_do(unlink, worker_id); +} + +void StringDedup::threads_do(ThreadClosure* tc) { + assert(is_enabled(), "String deduplication not enabled"); + tc->do_thread(StringDedupThread::thread()); +} + +void StringDedup::print_worker_threads_on(outputStream* st) { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupThread::thread()->print_on(st); + st->cr(); +} + +void StringDedup::verify() { + assert(is_enabled(), "String deduplication not enabled"); + StringDedupQueue::verify(); + StringDedupTable::verify(); +} + + +StringDedupUnlinkOrOopsDoClosure::StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, + OopClosure* keep_alive) : + _is_alive(is_alive), _keep_alive(keep_alive) { +} diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp new file mode 100644 index 00000000000..2125224008f --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP + +// +// String Deduplication +// +// String deduplication aims to reduce the heap live-set by deduplicating identical +// instances of String so that they share the same backing character array. +// +// The deduplication process is divided in two main parts, 1) finding the objects to +// deduplicate, and 2) deduplicating those objects. The first part is done as part of +// a normal GC cycle when objects are marked or evacuated. At this time a check is +// applied on each object to check if it is a candidate for deduplication. If so, the +// object is placed on the deduplication queue for later processing. The second part, +// processing the objects on the deduplication queue, is a concurrent phase which +// starts right after the stop-the-wold marking/evacuation phase. This phase is +// executed by the deduplication thread, which pulls deduplication candidates of the +// deduplication queue and tries to deduplicate them. +// +// A deduplication hashtable is used to keep track of all unique character arrays +// used by String objects. When deduplicating, a lookup is made in this table to see +// if there is already an identical character array somewhere on the heap. If so, the +// String object is adjusted to point to that character array, releasing the reference +// to the original array allowing it to eventually be garbage collected. If the lookup +// fails the character array is instead inserted into the hashtable so that this array +// can be shared at some point in the future. +// +// Candidate selection criteria is GC specific. +// +// Interned strings are a bit special. They are explicitly deduplicated just before +// being inserted into the StringTable (to avoid counteracting C2 optimizations done +// on string literals), then they also become deduplication candidates if they reach +// the deduplication age threshold or are evacuated to an old heap region. The second +// attempt to deduplicate such strings will be in vain, but we have no fast way of +// filtering them out. This has not shown to be a problem, as the number of interned +// strings is usually dwarfed by the number of normal (non-interned) strings. +// +// For additional information on string deduplication, please see JEP 192, +// http://openjdk.java.net/jeps/192 +// + +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +#include "gc/shared/stringdedup/stringDedupStat.hpp" +#include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "memory/allocation.hpp" +#include "runtime/thread.hpp" + +// +// Main interface for interacting with string deduplication. +// +class StringDedup : public AllStatic { +private: + // Single state for checking if string deduplication is enabled. + static bool _enabled; + +public: + // Returns true if string deduplication is enabled. + static bool is_enabled() { + return _enabled; + } + + // Stop the deduplication thread. + static void stop(); + + // Immediately deduplicates the given String object, bypassing the + // the deduplication queue. + static void deduplicate(oop java_string); + + static void parallel_unlink(StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id); + + static void threads_do(ThreadClosure* tc); + static void print_worker_threads_on(outputStream* st); + static void verify(); + + // GC support + static void gc_prologue(bool resize_and_rehash_table); + static void gc_epilogue(); + +protected: + // Initialize string deduplication. + // QUEUE: String Dedup Queue implementation + // STAT: String Dedup Stat implementation + template + static void initialize_impl(); +}; + +// +// This closure encapsulates the closures needed when scanning +// the deduplication queue and table during the unlink_or_oops_do() operation. +// +class StringDedupUnlinkOrOopsDoClosure : public StackObj { +private: + BoolObjectClosure* _is_alive; + OopClosure* _keep_alive; + +public: + StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, + OopClosure* keep_alive); + + // Applies and returns the result from the is_alive closure, or + // returns true if no such closure was provided. + bool is_alive(oop o) { + if (_is_alive != NULL) { + return _is_alive->do_object_b(o); + } + return true; + } + + // Applies the keep_alive closure, or does nothing if no such + // closure was provided. + void keep_alive(oop* p) { + if (_keep_alive != NULL) { + _keep_alive->do_oop(p); + } + } +}; + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.inline.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.inline.hpp new file mode 100644 index 00000000000..6db91d953e9 --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.inline.hpp @@ -0,0 +1,41 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP + +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupThread.inline.hpp" + +template +void StringDedup::initialize_impl() { + if (UseStringDeduplication) { + _enabled = true; + StringDedupQueue::create(); + StringDedupTable::create(); + StringDedupThreadImpl::create(); + } +} + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp new file mode 100644 index 00000000000..b58357aed87 --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp @@ -0,0 +1,66 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" + +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +#include "runtime/atomic.hpp" + +StringDedupQueue* StringDedupQueue::_queue = NULL; +volatile size_t StringDedupQueue::_claimed_index = 0; + +size_t StringDedupQueue::claim() { + return Atomic::add(size_t(1), &_claimed_index) - 1; +} + +void StringDedupQueue::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl) { + size_t claimed_queue = claim(); + while (claimed_queue < queue()->num_queues()) { + queue()->unlink_or_oops_do_impl(cl, claimed_queue); + claimed_queue = claim(); + } +} + +void StringDedupQueue::print_statistics() { + queue()->print_statistics_impl(); +} + +void StringDedupQueue::verify() { + queue()->verify_impl(); +} + +StringDedupQueue* const StringDedupQueue::queue() { + assert(_queue != NULL, "Not yet initialized"); + return _queue; +} + + +void StringDedupQueue::gc_prologue() { + _claimed_index = 0; +} + +void StringDedupQueue::gc_epilogue() { + assert(_claimed_index >= queue()->num_queues() || _claimed_index == 0, "All or nothing"); +} diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp new file mode 100644 index 00000000000..02b05de7640 --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp @@ -0,0 +1,112 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" + +class StringDedupUnlinkOrOopsDoClosure; + +// +// The deduplication queue acts as the communication channel between mark/evacuation +// phase and the concurrent deduplication phase. Deduplication candidates +// found during mark/evacuation are placed on this queue for later processing in the +// deduplication thread. A queue entry is an oop pointing to a String object (as opposed +// to entries in the deduplication hashtable which points to character arrays). +// +// While users of the queue treat it as a single queue, it is implemented as a set of +// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue +// operations by the GC workers. +// +// The oops in the queue are treated as weak pointers, meaning the objects they point to +// can become unreachable and pruned (cleared) before being popped by the deduplication +// thread. +// +// Pushing to the queue is thread safe (this relies on each thread using a unique worker +// id). Popping from the queue is NOT thread safe and can only be done by the deduplication +// thread outside a safepoint. +// + +class StringDedupQueue : public CHeapObj { +private: + static StringDedupQueue* _queue; + static volatile size_t _claimed_index; + +public: + template + static void create(); + + // Blocks and waits for the queue to become non-empty. + static inline void wait(); + + // Wakes up any thread blocked waiting for the queue to become non-empty. + static inline void cancel_wait(); + + // Pushes a deduplication candidate onto a specific GC worker queue. + static inline void push(uint worker_id, oop java_string); + + // Pops a deduplication candidate from any queue, returns NULL if + // all queues are empty. + static inline oop pop(); + + static void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl); + + static void print_statistics(); + static void verify(); + + // GC support + static void gc_prologue(); + static void gc_epilogue(); + +protected: + static StringDedupQueue* const queue(); + + // Queue interface. + + // Blocks and waits for the queue to become non-empty. + virtual void wait_impl() = 0; + + // Wakes up any thread blocked waiting for the queue to become non-empty. + virtual void cancel_wait_impl() = 0; + + // Pushes a deduplication candidate onto a specific GC worker queue. + virtual void push_impl(uint worker_id, oop java_string) = 0; + + // Pops a deduplication candidate from any queue, returns NULL if + // all queues are empty. + virtual oop pop_impl() = 0; + + virtual void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) = 0; + + virtual void print_statistics_impl() = 0; + virtual void verify_impl() = 0; + + virtual size_t num_queues() const = 0; + + static size_t claim(); +}; + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP diff --git a/test/hotspot/jtreg/vmTestbase/heapdump/JMapHeapCore/TestDescription.java b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.inline.hpp similarity index 52% rename from test/hotspot/jtreg/vmTestbase/heapdump/JMapHeapCore/TestDescription.java rename to src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.inline.hpp index 0a9ce569613..bfc3ebd820f 100644 --- a/test/hotspot/jtreg/vmTestbase/heapdump/JMapHeapCore/TestDescription.java +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -19,27 +19,36 @@ * Please 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_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP -/* - * @test - * - * @summary converted from VM testbase heapdump/JMapHeapCore. - * VM testbase keywords: [heapdump, feature_heapdump, nonconcurrent.jdk, quick, quarantine] - * VM testbase comments: JDK-8023376 JDK-8001227 JDK-8051445 - * VM testbase readme: - * DESCRIPTION - * This test verifies that heap dump created by jhsdb is able to be - * parsed by HprofParser. It fills the heap with objects of different types - * till OutOfMemoryError, forces core dump, then uses jhsdb on core file - * to create heap dump and then verifies created heap dump with HprofParser. - * - * @library /vmTestbase - * /test/lib - * @run driver jdk.test.lib.FileInstaller . . - * @build jdk.test.lib.hprof.HprofParser - * heapdump.share.EatMemory - * @run shell/timeout=300 run.sh - */ +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +template +void StringDedupQueue::create() { + assert(StringDedup::is_enabled(), "Must be enabled"); + assert(_queue == NULL, "Can have only one queue"); + _queue = new Q; +} + +void StringDedupQueue::wait() { + queue()->wait_impl(); +} + +void StringDedupQueue::cancel_wait() { + queue()->cancel_wait_impl(); +} + +void StringDedupQueue::push(uint worker_id, oop java_string) { + queue()->push_impl(worker_id, java_string); +} + +oop StringDedupQueue::pop() { + return queue()->pop_impl(); +} + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp new file mode 100644 index 00000000000..5a896b5686c --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/stringdedup/stringDedupStat.hpp" +#include "logging/log.hpp" + +StringDedupStat::StringDedupStat() : + _inspected(0), + _skipped(0), + _hashed(0), + _known(0), + _new(0), + _new_bytes(0), + _deduped(0), + _deduped_bytes(0), + _idle(0), + _exec(0), + _block(0), + _start_concurrent(0.0), + _end_concurrent(0.0), + _start_phase(0.0), + _idle_elapsed(0.0), + _exec_elapsed(0.0), + _block_elapsed(0.0) { +} + +void StringDedupStat::add(const StringDedupStat* const stat) { + _inspected += stat->_inspected; + _skipped += stat->_skipped; + _hashed += stat->_hashed; + _known += stat->_known; + _new += stat->_new; + _new_bytes += stat->_new_bytes; + _deduped += stat->_deduped; + _deduped_bytes += stat->_deduped_bytes; + _idle += stat->_idle; + _exec += stat->_exec; + _block += stat->_block; + _idle_elapsed += stat->_idle_elapsed; + _exec_elapsed += stat->_exec_elapsed; + _block_elapsed += stat->_block_elapsed; +} + +void StringDedupStat::print_start(const StringDedupStat* last_stat) { + log_info(gc, stringdedup)( + "Concurrent String Deduplication (" STRDEDUP_TIME_FORMAT ")", + STRDEDUP_TIME_PARAM(last_stat->_start_concurrent)); +} + +void StringDedupStat::print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat) { + double total_deduped_bytes_percent = 0.0; + + if (total_stat->_new_bytes > 0) { + // Avoid division by zero + total_deduped_bytes_percent = percent_of(total_stat->_deduped_bytes, total_stat->_new_bytes); + } + + log_info(gc, stringdedup)( + "Concurrent String Deduplication " + STRDEDUP_BYTES_FORMAT_NS "->" STRDEDUP_BYTES_FORMAT_NS "(" STRDEDUP_BYTES_FORMAT_NS ") " + "avg " STRDEDUP_PERCENT_FORMAT_NS " " + "(" STRDEDUP_TIME_FORMAT ", " STRDEDUP_TIME_FORMAT ") " STRDEDUP_TIME_FORMAT_MS, + STRDEDUP_BYTES_PARAM(last_stat->_new_bytes), + STRDEDUP_BYTES_PARAM(last_stat->_new_bytes - last_stat->_deduped_bytes), + STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes), + total_deduped_bytes_percent, + STRDEDUP_TIME_PARAM(last_stat->_start_concurrent), + STRDEDUP_TIME_PARAM(last_stat->_end_concurrent), + STRDEDUP_TIME_PARAM_MS(last_stat->_exec_elapsed)); +} + +void StringDedupStat::reset() { + _inspected = 0; + _skipped = 0; + _hashed = 0; + _known = 0; + _new = 0; + _new_bytes = 0; + _deduped = 0; + _deduped_bytes = 0; + _idle = 0; + _exec = 0; + _block = 0; + _start_concurrent = 0.0; + _end_concurrent = 0.0; + _start_phase = 0.0; + _idle_elapsed = 0.0; + _exec_elapsed = 0.0; + _block_elapsed = 0.0; +} + +void StringDedupStat::print_statistics(bool total) const { + double skipped_percent = percent_of(_skipped, _inspected); + double hashed_percent = percent_of(_hashed, _inspected); + double known_percent = percent_of(_known, _inspected); + double new_percent = percent_of(_new, _inspected); + double deduped_percent = percent_of(_deduped, _new); + double deduped_bytes_percent = percent_of(_deduped_bytes, _new_bytes); +/* + double deduped_young_percent = percent_of(stat._deduped_young, stat._deduped); + double deduped_young_bytes_percent = percent_of(stat._deduped_young_bytes, stat._deduped_bytes); + double deduped_old_percent = percent_of(stat._deduped_old, stat._deduped); + double deduped_old_bytes_percent = percent_of(stat._deduped_old_bytes, stat._deduped_bytes); +*/ + if (total) { + log_debug(gc, stringdedup)( + " Total Exec: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS + ", Idle: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS + ", Blocked: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS, + _exec, STRDEDUP_TIME_PARAM_MS(_exec_elapsed), + _idle, STRDEDUP_TIME_PARAM_MS(_idle_elapsed), + _block, STRDEDUP_TIME_PARAM_MS(_block_elapsed)); + } else { + log_debug(gc, stringdedup)( + " Last Exec: " STRDEDUP_TIME_FORMAT_MS + ", Idle: " STRDEDUP_TIME_FORMAT_MS + ", Blocked: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS, + STRDEDUP_TIME_PARAM_MS(_exec_elapsed), + STRDEDUP_TIME_PARAM_MS(_idle_elapsed), + _block, STRDEDUP_TIME_PARAM_MS(_block_elapsed)); + } + log_debug(gc, stringdedup)(" Inspected: " STRDEDUP_OBJECTS_FORMAT, _inspected); + log_debug(gc, stringdedup)(" Skipped: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _skipped, skipped_percent); + log_debug(gc, stringdedup)(" Hashed: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _hashed, hashed_percent); + log_debug(gc, stringdedup)(" Known: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _known, known_percent); + log_debug(gc, stringdedup)(" New: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT, + _new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes)); + log_debug(gc, stringdedup)(" Deduplicated: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", + _deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent); +} diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp new file mode 100644 index 00000000000..4cbb034314a --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP + +#include "memory/allocation.hpp" +#include "runtime/os.hpp" + +// Macros for GC log output formating +#define STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12) +#define STRDEDUP_TIME_FORMAT "%.3fs" +#define STRDEDUP_TIME_PARAM(time) (time) +#define STRDEDUP_TIME_FORMAT_MS "%.3fms" +#define STRDEDUP_TIME_PARAM_MS(time) ((time) * MILLIUNITS) +#define STRDEDUP_PERCENT_FORMAT "%5.1f%%" +#define STRDEDUP_PERCENT_FORMAT_NS "%.1f%%" +#define STRDEDUP_BYTES_FORMAT "%8.1f%s" +#define STRDEDUP_BYTES_FORMAT_NS "%.1f%s" +#define STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes)) + +// +// Statistics gathered by the deduplication thread. +// +class StringDedupStat : public CHeapObj { +protected: + // Counters + uintx _inspected; + uintx _skipped; + uintx _hashed; + uintx _known; + uintx _new; + uintx _new_bytes; + uintx _deduped; + uintx _deduped_bytes; + uintx _idle; + uintx _exec; + uintx _block; + + // Time spent by the deduplication thread in different phases + double _start_concurrent; + double _end_concurrent; + double _start_phase; + double _idle_elapsed; + double _exec_elapsed; + double _block_elapsed; + +public: + StringDedupStat(); + + void inc_inspected() { + _inspected++; + } + + void inc_skipped() { + _skipped++; + } + + void inc_hashed() { + _hashed++; + } + + void inc_known() { + _known++; + } + + void inc_new(uintx bytes) { + _new++; + _new_bytes += bytes; + } + + virtual void deduped(oop obj, uintx bytes) { + _deduped++; + _deduped_bytes += bytes; + } + + void mark_idle() { + _start_phase = os::elapsedTime(); + _idle++; + } + + void mark_exec() { + double now = os::elapsedTime(); + _idle_elapsed = now - _start_phase; + _start_phase = now; + _start_concurrent = now; + _exec++; + } + + void mark_block() { + double now = os::elapsedTime(); + _exec_elapsed += now - _start_phase; + _start_phase = now; + _block++; + } + + void mark_unblock() { + double now = os::elapsedTime(); + _block_elapsed += now - _start_phase; + _start_phase = now; + } + + void mark_done() { + double now = os::elapsedTime(); + _exec_elapsed += now - _start_phase; + _end_concurrent = now; + } + + virtual void reset(); + virtual void add(const StringDedupStat* const stat); + virtual void print_statistics(bool total) const; + + static void print_start(const StringDedupStat* last_stat); + static void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat); +}; + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP + diff --git a/src/hotspot/share/gc/g1/g1StringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp similarity index 65% rename from src/hotspot/share/gc/g1/g1StringDedupTable.cpp rename to src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 1e4adcce7d7..a5674e04d03 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -25,12 +25,12 @@ #include "precompiled.hpp" #include "classfile/altHashing.hpp" #include "classfile/javaClasses.inline.hpp" -#include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1StringDedup.hpp" -#include "gc/g1/g1StringDedupTable.hpp" +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "gc/shared/suspendibleThreadSet.hpp" #include "logging/log.hpp" #include "memory/padded.inline.hpp" +#include "oops/access.inline.hpp" #include "oops/arrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.hpp" @@ -41,25 +41,25 @@ // List of deduplication table entries. Links table // entries together using their _next fields. // -class G1StringDedupEntryList : public CHeapObj { +class StringDedupEntryList : public CHeapObj { private: - G1StringDedupEntry* _list; + StringDedupEntry* _list; size_t _length; public: - G1StringDedupEntryList() : + StringDedupEntryList() : _list(NULL), _length(0) { } - void add(G1StringDedupEntry* entry) { + void add(StringDedupEntry* entry) { entry->set_next(_list); _list = entry; _length++; } - G1StringDedupEntry* remove() { - G1StringDedupEntry* entry = _list; + StringDedupEntry* remove() { + StringDedupEntry* entry = _list; if (entry != NULL) { _list = entry->next(); _length--; @@ -67,8 +67,8 @@ public: return entry; } - G1StringDedupEntry* remove_all() { - G1StringDedupEntry* list = _list; + StringDedupEntry* remove_all() { + StringDedupEntry* list = _list; _list = NULL; return list; } @@ -92,28 +92,28 @@ public: // Allocations are synchronized by StringDedupTable_lock as part of a table // modification. // -class G1StringDedupEntryCache : public CHeapObj { +class StringDedupEntryCache : public CHeapObj { private: // One cache/overflow list per GC worker to allow lock less freeing of // entries while doing a parallel scan of the table. Using PaddedEnd to // avoid false sharing. size_t _nlists; size_t _max_list_length; - PaddedEnd* _cached; - PaddedEnd* _overflowed; + PaddedEnd* _cached; + PaddedEnd* _overflowed; public: - G1StringDedupEntryCache(size_t max_size); - ~G1StringDedupEntryCache(); + StringDedupEntryCache(size_t max_size); + ~StringDedupEntryCache(); // Set max number of table entries to cache. void set_max_size(size_t max_size); // Get a table entry from the cache, or allocate a new entry if the cache is empty. - G1StringDedupEntry* alloc(); + StringDedupEntry* alloc(); // Insert a table entry into the cache. - void free(G1StringDedupEntry* entry, uint worker_id); + void free(StringDedupEntry* entry, uint worker_id); // Returns current number of entries in the cache. size_t size(); @@ -122,33 +122,33 @@ public: void delete_overflowed(); }; -G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) : +StringDedupEntryCache::StringDedupEntryCache(size_t max_size) : _nlists(ParallelGCThreads), _max_list_length(0), - _cached(PaddedArray::create_unfreeable((uint)_nlists)), - _overflowed(PaddedArray::create_unfreeable((uint)_nlists)) { + _cached(PaddedArray::create_unfreeable((uint)_nlists)), + _overflowed(PaddedArray::create_unfreeable((uint)_nlists)) { set_max_size(max_size); } -G1StringDedupEntryCache::~G1StringDedupEntryCache() { +StringDedupEntryCache::~StringDedupEntryCache() { ShouldNotReachHere(); } -void G1StringDedupEntryCache::set_max_size(size_t size) { +void StringDedupEntryCache::set_max_size(size_t size) { _max_list_length = size / _nlists; } -G1StringDedupEntry* G1StringDedupEntryCache::alloc() { +StringDedupEntry* StringDedupEntryCache::alloc() { for (size_t i = 0; i < _nlists; i++) { - G1StringDedupEntry* entry = _cached[i].remove(); + StringDedupEntry* entry = _cached[i].remove(); if (entry != NULL) { return entry; } } - return new G1StringDedupEntry(); + return new StringDedupEntry(); } -void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) { +void StringDedupEntryCache::free(StringDedupEntry* entry, uint worker_id) { assert(entry->obj() != NULL, "Double free"); assert(worker_id < _nlists, "Invalid worker id"); @@ -164,7 +164,7 @@ void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) { } } -size_t G1StringDedupEntryCache::size() { +size_t StringDedupEntryCache::size() { size_t size = 0; for (size_t i = 0; i < _nlists; i++) { size += _cached[i].length(); @@ -172,12 +172,12 @@ size_t G1StringDedupEntryCache::size() { return size; } -void G1StringDedupEntryCache::delete_overflowed() { +void StringDedupEntryCache::delete_overflowed() { double start = os::elapsedTime(); uintx count = 0; for (size_t i = 0; i < _nlists; i++) { - G1StringDedupEntry* entry; + StringDedupEntry* entry; { // The overflow list can be modified during safepoints, therefore @@ -189,7 +189,7 @@ void G1StringDedupEntryCache::delete_overflowed() { // Delete all entries while (entry != NULL) { - G1StringDedupEntry* next = entry->next(); + StringDedupEntry* next = entry->next(); delete entry; entry = next; count++; @@ -197,27 +197,31 @@ void G1StringDedupEntryCache::delete_overflowed() { } double end = os::elapsedTime(); - log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT_MS, - count, G1_STRDEDUP_TIME_PARAM_MS(end - start)); + log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " STRDEDUP_TIME_FORMAT_MS, + count, STRDEDUP_TIME_PARAM_MS(end - start)); } -G1StringDedupTable* G1StringDedupTable::_table = NULL; -G1StringDedupEntryCache* G1StringDedupTable::_entry_cache = NULL; +StringDedupTable* StringDedupTable::_table = NULL; +StringDedupEntryCache* StringDedupTable::_entry_cache = NULL; -const size_t G1StringDedupTable::_min_size = (1 << 10); // 1024 -const size_t G1StringDedupTable::_max_size = (1 << 24); // 16777216 -const double G1StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load -const double G1StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load -const double G1StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size -const uintx G1StringDedupTable::_rehash_multiple = 60; // Hash bucket has 60 times more collisions than expected -const uintx G1StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor); +const size_t StringDedupTable::_min_size = (1 << 10); // 1024 +const size_t StringDedupTable::_max_size = (1 << 24); // 16777216 +const double StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load +const double StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load +const double StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size +const uintx StringDedupTable::_rehash_multiple = 60; // Hash bucket has 60 times more collisions than expected +const uintx StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor); -uintx G1StringDedupTable::_entries_added = 0; -uintx G1StringDedupTable::_entries_removed = 0; -uintx G1StringDedupTable::_resize_count = 0; -uintx G1StringDedupTable::_rehash_count = 0; +uintx StringDedupTable::_entries_added = 0; +uintx StringDedupTable::_entries_removed = 0; +uintx StringDedupTable::_resize_count = 0; +uintx StringDedupTable::_rehash_count = 0; -G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) : +StringDedupTable* StringDedupTable::_resized_table = NULL; +StringDedupTable* StringDedupTable::_rehashed_table = NULL; +volatile size_t StringDedupTable::_claimed_index = 0; + +StringDedupTable::StringDedupTable(size_t size, jint hash_seed) : _size(size), _entries(0), _grow_threshold((uintx)(size * _grow_load_factor)), @@ -225,22 +229,22 @@ G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) : _rehash_needed(false), _hash_seed(hash_seed) { assert(is_power_of_2(size), "Table size must be a power of 2"); - _buckets = NEW_C_HEAP_ARRAY(G1StringDedupEntry*, _size, mtGC); - memset(_buckets, 0, _size * sizeof(G1StringDedupEntry*)); + _buckets = NEW_C_HEAP_ARRAY(StringDedupEntry*, _size, mtGC); + memset(_buckets, 0, _size * sizeof(StringDedupEntry*)); } -G1StringDedupTable::~G1StringDedupTable() { +StringDedupTable::~StringDedupTable() { FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets); } -void G1StringDedupTable::create() { +void StringDedupTable::create() { assert(_table == NULL, "One string deduplication table allowed"); - _entry_cache = new G1StringDedupEntryCache(_min_size * _max_cache_factor); - _table = new G1StringDedupTable(_min_size); + _entry_cache = new StringDedupEntryCache(_min_size * _max_cache_factor); + _table = new StringDedupTable(_min_size); } -void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list) { - G1StringDedupEntry* entry = _entry_cache->alloc(); +void StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, StringDedupEntry** list) { + StringDedupEntry* entry = _entry_cache->alloc(); entry->set_obj(value); entry->set_hash(hash); entry->set_latin1(latin1); @@ -249,38 +253,41 @@ void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, _entries++; } -void G1StringDedupTable::remove(G1StringDedupEntry** pentry, uint worker_id) { - G1StringDedupEntry* entry = *pentry; +void StringDedupTable::remove(StringDedupEntry** pentry, uint worker_id) { + StringDedupEntry* entry = *pentry; *pentry = entry->next(); _entry_cache->free(entry, worker_id); } -void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest) { - G1StringDedupEntry* entry = *pentry; +void StringDedupTable::transfer(StringDedupEntry** pentry, StringDedupTable* dest) { + StringDedupEntry* entry = *pentry; *pentry = entry->next(); unsigned int hash = entry->hash(); size_t index = dest->hash_to_index(hash); - G1StringDedupEntry** list = dest->bucket(index); + StringDedupEntry** list = dest->bucket(index); entry->set_next(*list); *list = entry; } -bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) { - return (value1 == value2 || +bool StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) { + return (oopDesc::equals(value1, value2) || (value1->length() == value2->length() && - (!memcmp(value1->base(T_BYTE), + (!memcmp(value1->base(T_BYTE), value2->base(T_BYTE), value1->length() * sizeof(jbyte))))); } -typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned int hash, - G1StringDedupEntry** list, uintx &count) { - for (G1StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) { +typeArrayOop StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned int hash, + StringDedupEntry** list, uintx &count) { + for (StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) { if (entry->hash() == hash && entry->latin1() == latin1) { typeArrayOop existing_value = entry->obj(); if (equals(value, existing_value)) { - // Match found - return existing_value; + // Apply proper barrier to make sure it is kept alive. Concurrent mark might + // otherwise declare it dead if there are no other strong references to this object. + oop* obj_addr = (oop*)entry->obj_addr(); + oop obj = RootAccess::oop_load(obj_addr); + return typeArrayOop(obj); } } count++; @@ -290,9 +297,9 @@ typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigne return NULL; } -typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) { +typeArrayOop StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) { size_t index = hash_to_index(hash); - G1StringDedupEntry** list = bucket(index); + StringDedupEntry** list = bucket(index); uintx count = 0; // Lookup in list @@ -314,7 +321,7 @@ typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool la return existing_value; } -unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) { +unsigned int StringDedupTable::hash_code(typeArrayOop value, bool latin1) { unsigned int hash; int length = value->length(); if (latin1) { @@ -337,16 +344,16 @@ unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) { return hash; } -void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) { +void StringDedupTable::deduplicate(oop java_string, StringDedupStat* stat) { assert(java_lang_String::is_instance(java_string), "Must be a string"); NoSafepointVerifier nsv; - stat.inc_inspected(); + stat->inc_inspected(); typeArrayOop value = java_lang_String::value(java_string); if (value == NULL) { // String has no value - stat.inc_skipped(); + stat->inc_skipped(); return; } @@ -361,7 +368,7 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) { if (hash == 0) { // Compute hash hash = hash_code(value, latin1); - stat.inc_hashed(); + stat->inc_hashed(); if (use_java_hash() && hash != 0) { // Store hash code in cache @@ -372,31 +379,30 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) { typeArrayOop existing_value = lookup_or_add(value, latin1, hash); if (existing_value == value) { // Same value, already known - stat.inc_known(); + stat->inc_known(); return; } // Get size of value array uintx size_in_bytes = value->size() * HeapWordSize; - stat.inc_new(size_in_bytes); + stat->inc_new(size_in_bytes); if (existing_value != NULL) { - // Enqueue the reference to make sure it is kept alive. Concurrent mark might - // otherwise declare it dead if there are no other strong references to this object. - G1BarrierSet::enqueue(existing_value); - // Existing value found, deduplicate string java_lang_String::set_value(java_string, existing_value); - - if (G1CollectedHeap::heap()->is_in_young(value)) { - stat.inc_deduped_young(size_in_bytes); - } else { - stat.inc_deduped_old(size_in_bytes); - } + stat->deduped(value, size_in_bytes); } } -G1StringDedupTable* G1StringDedupTable::prepare_resize() { +bool StringDedupTable::is_resizing() { + return _resized_table != NULL; +} + +bool StringDedupTable::is_rehashing() { + return _rehashed_table != NULL; +} + +StringDedupTable* StringDedupTable::prepare_resize() { size_t size = _table->_size; // Check if the hashtable needs to be resized @@ -434,10 +440,10 @@ G1StringDedupTable* G1StringDedupTable::prepare_resize() { // Allocate the new table. The new table will be populated by workers // calling unlink_or_oops_do() and finally installed by finish_resize(). - return new G1StringDedupTable(size, _table->_hash_seed); + return new StringDedupTable(size, _table->_hash_seed); } -void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) { +void StringDedupTable::finish_resize(StringDedupTable* resized_table) { assert(resized_table != NULL, "Invalid table"); resized_table->_entries = _table->_entries; @@ -449,7 +455,7 @@ void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) { _table = resized_table; } -void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) { +void StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) { // The table is divided into partitions to allow lock-less parallel processing by // multiple worker threads. A worker thread first claims a partition, which ensures // exclusive access to that part of the table, then continues to process it. To allow @@ -464,7 +470,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c size_t table_half = _table->_size / 2; // Let each partition be one page worth of buckets - size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(G1StringDedupEntry*)); + size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(StringDedupEntry*)); assert(table_half % partition_size == 0, "Invalid partition size"); // Number of entries removed during the scan @@ -472,7 +478,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c for (;;) { // Grab next partition to scan - size_t partition_begin = cl->claim_table_partition(partition_size); + size_t partition_begin = claim_table_partition(partition_size); size_t partition_end = partition_begin + partition_size; if (partition_begin >= table_half) { // End of table @@ -492,22 +498,22 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c } } -uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, - size_t partition_begin, - size_t partition_end, - uint worker_id) { +uintx StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, + size_t partition_begin, + size_t partition_end, + uint worker_id) { uintx removed = 0; for (size_t bucket = partition_begin; bucket < partition_end; bucket++) { - G1StringDedupEntry** entry = _table->bucket(bucket); + StringDedupEntry** entry = _table->bucket(bucket); while (*entry != NULL) { oop* p = (oop*)(*entry)->obj_addr(); if (cl->is_alive(*p)) { cl->keep_alive(p); - if (cl->is_resizing()) { + if (is_resizing()) { // We are resizing the table, transfer entry to the new table - _table->transfer(entry, cl->resized_table()); + _table->transfer(entry, _resized_table); } else { - if (cl->is_rehashing()) { + if (is_rehashing()) { // We are rehashing the table, rehash the entry but keep it // in the table. We can't transfer entries into the new table // at this point since we don't have exclusive access to all @@ -533,7 +539,34 @@ uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* return removed; } -G1StringDedupTable* G1StringDedupTable::prepare_rehash() { +void StringDedupTable::gc_prologue(bool resize_and_rehash_table) { + assert(!is_resizing() && !is_rehashing(), "Already in progress?"); + + _claimed_index = 0; + if (resize_and_rehash_table) { + // If both resize and rehash is needed, only do resize. Rehash of + // the table will eventually happen if the situation persists. + _resized_table = StringDedupTable::prepare_resize(); + if (!is_resizing()) { + _rehashed_table = StringDedupTable::prepare_rehash(); + } + } +} + +void StringDedupTable::gc_epilogue() { + assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash"); + assert(_claimed_index >= _table->_size / 2 || _claimed_index == 0, "All or nothing"); + + if (is_resizing()) { + StringDedupTable::finish_resize(_resized_table); + _resized_table = NULL; + } else if (is_rehashing()) { + StringDedupTable::finish_rehash(_rehashed_table); + _rehashed_table = NULL; + } +} + +StringDedupTable* StringDedupTable::prepare_rehash() { if (!_table->_rehash_needed && !StringDeduplicationRehashALot) { // Rehash not needed return NULL; @@ -546,15 +579,15 @@ G1StringDedupTable* G1StringDedupTable::prepare_rehash() { _table->_hash_seed = AltHashing::compute_seed(); // Allocate the new table, same size and hash seed - return new G1StringDedupTable(_table->_size, _table->_hash_seed); + return new StringDedupTable(_table->_size, _table->_hash_seed); } -void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) { +void StringDedupTable::finish_rehash(StringDedupTable* rehashed_table) { assert(rehashed_table != NULL, "Invalid table"); // Move all newly rehashed entries into the correct buckets in the new table for (size_t bucket = 0; bucket < _table->_size; bucket++) { - G1StringDedupEntry** entry = _table->bucket(bucket); + StringDedupEntry** entry = _table->bucket(bucket); while (*entry != NULL) { _table->transfer(entry, rehashed_table); } @@ -569,14 +602,18 @@ void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) { _table = rehashed_table; } -void G1StringDedupTable::verify() { +size_t StringDedupTable::claim_table_partition(size_t partition_size) { + return Atomic::add(partition_size, &_claimed_index) - partition_size; +} + +void StringDedupTable::verify() { for (size_t bucket = 0; bucket < _table->_size; bucket++) { // Verify entries - G1StringDedupEntry** entry = _table->bucket(bucket); + StringDedupEntry** entry = _table->bucket(bucket); while (*entry != NULL) { typeArrayOop value = (*entry)->obj(); guarantee(value != NULL, "Object must not be NULL"); - guarantee(G1CollectedHeap::heap()->is_in_reserved(value), "Object must be on the heap"); + guarantee(Universe::heap()->is_in_reserved(value), "Object must be on the heap"); guarantee(!value->is_forwarded(), "Object must not be forwarded"); guarantee(value->is_typeArray(), "Object must be a typeArrayOop"); bool latin1 = (*entry)->latin1(); @@ -590,11 +627,11 @@ void G1StringDedupTable::verify() { // We only need to compare entries in the same bucket. If the same oop or an // identical array has been inserted more than once into different/incorrect // buckets the verification step above will catch that. - G1StringDedupEntry** entry1 = _table->bucket(bucket); + StringDedupEntry** entry1 = _table->bucket(bucket); while (*entry1 != NULL) { typeArrayOop value1 = (*entry1)->obj(); bool latin1_1 = (*entry1)->latin1(); - G1StringDedupEntry** entry2 = (*entry1)->next_addr(); + StringDedupEntry** entry2 = (*entry1)->next_addr(); while (*entry2 != NULL) { typeArrayOop value2 = (*entry2)->obj(); bool latin1_2 = (*entry2)->latin1(); @@ -606,19 +643,19 @@ void G1StringDedupTable::verify() { } } -void G1StringDedupTable::clean_entry_cache() { +void StringDedupTable::clean_entry_cache() { _entry_cache->delete_overflowed(); } -void G1StringDedupTable::print_statistics() { +void StringDedupTable::print_statistics() { Log(gc, stringdedup) log; log.debug(" Table"); - log.debug(" Memory Usage: " G1_STRDEDUP_BYTES_FORMAT_NS, - G1_STRDEDUP_BYTES_PARAM(_table->_size * sizeof(G1StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(G1StringDedupEntry))); + log.debug(" Memory Usage: " STRDEDUP_BYTES_FORMAT_NS, + STRDEDUP_BYTES_PARAM(_table->_size * sizeof(StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(StringDedupEntry))); log.debug(" Size: " SIZE_FORMAT ", Min: " SIZE_FORMAT ", Max: " SIZE_FORMAT, _table->_size, _min_size, _max_size); - log.debug(" Entries: " UINTX_FORMAT ", Load: " G1_STRDEDUP_PERCENT_FORMAT_NS ", Cached: " UINTX_FORMAT ", Added: " UINTX_FORMAT ", Removed: " UINTX_FORMAT, + log.debug(" Entries: " UINTX_FORMAT ", Load: " STRDEDUP_PERCENT_FORMAT_NS ", Cached: " UINTX_FORMAT ", Added: " UINTX_FORMAT ", Removed: " UINTX_FORMAT, _table->_entries, percent_of(_table->_entries, _table->_size), _entry_cache->size(), _entries_added, _entries_removed); - log.debug(" Resize Count: " UINTX_FORMAT ", Shrink Threshold: " UINTX_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT_NS ")", + log.debug(" Resize Count: " UINTX_FORMAT ", Shrink Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS ")", _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0); log.debug(" Rehash Count: " UINTX_FORMAT ", Rehash Threshold: " UINTX_FORMAT ", Hash Seed: 0x%x", _rehash_count, _rehash_threshold, _table->_hash_seed); log.debug(" Age Threshold: " UINTX_FORMAT, StringDeduplicationAgeThreshold); diff --git a/src/hotspot/share/gc/g1/g1StringDedupTable.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp similarity index 78% rename from src/hotspot/share/gc/g1/g1StringDedupTable.hpp rename to src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp index c30d0403ead..6a1db65fad5 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupTable.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -22,44 +22,44 @@ * */ -#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP -#define SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP -#include "gc/g1/g1StringDedupStat.hpp" +#include "gc/shared/stringdedup/stringDedupStat.hpp" #include "runtime/mutexLocker.hpp" -class G1StringDedupEntryCache; -class G1StringDedupUnlinkOrOopsDoClosure; +class StringDedupEntryCache; +class StringDedupUnlinkOrOopsDoClosure; // // Table entry in the deduplication hashtable. Points weakly to the // character array. Can be chained in a linked list in case of hash // collisions or when placed in a freelist in the entry cache. // -class G1StringDedupEntry : public CHeapObj { +class StringDedupEntry : public CHeapObj { private: - G1StringDedupEntry* _next; + StringDedupEntry* _next; unsigned int _hash; bool _latin1; typeArrayOop _obj; public: - G1StringDedupEntry() : + StringDedupEntry() : _next(NULL), _hash(0), _latin1(false), _obj(NULL) { } - G1StringDedupEntry* next() { + StringDedupEntry* next() { return _next; } - G1StringDedupEntry** next_addr() { + StringDedupEntry** next_addr() { return &_next; } - void set_next(G1StringDedupEntry* next) { + void set_next(StringDedupEntry* next) { _next = next; } @@ -111,16 +111,16 @@ public: // the table partition (i.e. a range of elements in _buckets), not other parts of the // table such as the _entries field, statistics counters, etc. // -class G1StringDedupTable : public CHeapObj { +class StringDedupTable : public CHeapObj { private: // The currently active hashtable instance. Only modified when // the table is resizes or rehashed. - static G1StringDedupTable* _table; + static StringDedupTable* _table; // Cache for reuse and fast alloc/free of table entries. - static G1StringDedupEntryCache* _entry_cache; + static StringDedupEntryCache* _entry_cache; - G1StringDedupEntry** _buckets; + StringDedupEntry** _buckets; size_t _size; uintx _entries; uintx _shrink_threshold; @@ -148,11 +148,16 @@ private: static uintx _resize_count; static uintx _rehash_count; - G1StringDedupTable(size_t size, jint hash_seed = 0); - ~G1StringDedupTable(); + static volatile size_t _claimed_index; + + static StringDedupTable* _resized_table; + static StringDedupTable* _rehashed_table; + + StringDedupTable(size_t size, jint hash_seed = 0); + ~StringDedupTable(); // Returns the hash bucket at the given index. - G1StringDedupEntry** bucket(size_t index) { + StringDedupEntry** bucket(size_t index) { return _buckets + index; } @@ -162,18 +167,18 @@ private: } // Adds a new table entry to the given hash bucket. - void add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list); + void add(typeArrayOop value, bool latin1, unsigned int hash, StringDedupEntry** list); // Removes the given table entry from the table. - void remove(G1StringDedupEntry** pentry, uint worker_id); + void remove(StringDedupEntry** pentry, uint worker_id); // Transfers a table entry from the current table to the destination table. - void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest); + void transfer(StringDedupEntry** pentry, StringDedupTable* dest); // Returns an existing character array in the given hash bucket, or NULL // if no matching character array exists. typeArrayOop lookup(typeArrayOop value, bool latin1, unsigned int hash, - G1StringDedupEntry** list, uintx &count); + StringDedupEntry** list, uintx &count); // Returns an existing character array in the table, or inserts a new // table entry if no matching character array exists. @@ -200,42 +205,51 @@ private: // currently active hash function and hash seed. static unsigned int hash_code(typeArrayOop value, bool latin1); - static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, + static uintx unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, size_t partition_begin, size_t partition_end, uint worker_id); + static size_t claim_table_partition(size_t partition_size); + + static bool is_resizing(); + static bool is_rehashing(); + + // If a table resize is needed, returns a newly allocated empty + // hashtable of the proper size. + static StringDedupTable* prepare_resize(); + + // Installs a newly resized table as the currently active table + // and deletes the previously active table. + static void finish_resize(StringDedupTable* resized_table); + + // If a table rehash is needed, returns a newly allocated empty + // hashtable and updates the hash seed. + static StringDedupTable* prepare_rehash(); + + // Transfers rehashed entries from the currently active table into + // the new table. Installs the new table as the currently active table + // and deletes the previously active table. + static void finish_rehash(StringDedupTable* rehashed_table); + public: static void create(); // Deduplicates the given String object, or adds its backing // character array to the deduplication hashtable. - static void deduplicate(oop java_string, G1StringDedupStat& stat); + static void deduplicate(oop java_string, StringDedupStat* stat); - // If a table resize is needed, returns a newly allocated empty - // hashtable of the proper size. - static G1StringDedupTable* prepare_resize(); + static void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id); - // Installs a newly resized table as the currently active table - // and deletes the previously active table. - static void finish_resize(G1StringDedupTable* resized_table); - - // If a table rehash is needed, returns a newly allocated empty - // hashtable and updates the hash seed. - static G1StringDedupTable* prepare_rehash(); - - // Transfers rehashed entries from the currently active table into - // the new table. Installs the new table as the currently active table - // and deletes the previously active table. - static void finish_rehash(G1StringDedupTable* rehashed_table); + static void print_statistics(); + static void verify(); // If the table entry cache has grown too large, delete overflowed entries. static void clean_entry_cache(); - static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id); - - static void print_statistics(); - static void verify(); + // GC support + static void gc_prologue(bool resize_and_rehash_table); + static void gc_epilogue(); }; -#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp new file mode 100644 index 00000000000..427bf05c92c --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/stringTable.hpp" +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.inline.hpp" +#include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" +#include "gc/shared/suspendibleThreadSet.hpp" +#include "logging/log.hpp" +#include "oops/access.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" + +StringDedupThread* StringDedupThread::_thread = NULL; + +StringDedupThread::StringDedupThread() : + ConcurrentGCThread() { + set_name("StrDedup"); + create_and_start(); +} + +StringDedupThread::~StringDedupThread() { + ShouldNotReachHere(); +} + +StringDedupThread* StringDedupThread::thread() { + assert(_thread != NULL, "String deduplication thread not created"); + return _thread; +} + +class StringDedupSharedClosure: public OopClosure { + private: + StringDedupStat* _stat; + + public: + StringDedupSharedClosure(StringDedupStat* stat) : _stat(stat) {} + + virtual void do_oop(oop* p) { ShouldNotReachHere(); } + virtual void do_oop(narrowOop* p) { + oop java_string = RawAccess<>::oop_load(p); + StringDedupTable::deduplicate(java_string, _stat); + } +}; + +// The CDS archive does not include the string dedupication table. Only the string +// table is saved in the archive. The shared strings from CDS archive need to be +// added to the string dedupication table before deduplication occurs. That is +// done in the begining of the StringDedupThread (see StringDedupThread::do_deduplication()). +void StringDedupThread::deduplicate_shared_strings(StringDedupStat* stat) { + StringDedupSharedClosure sharedStringDedup(stat); + StringTable::shared_oops_do(&sharedStringDedup); +} + +void StringDedupThread::stop_service() { + StringDedupQueue::cancel_wait(); +} + +void StringDedupThread::print_start(const StringDedupStat* last_stat) { + StringDedupStat::print_start(last_stat); +} + +void StringDedupThread::print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat) { + StringDedupStat::print_end(last_stat, total_stat); + if (log_is_enabled(Debug, gc, stringdedup)) { + last_stat->print_statistics(false); + total_stat->print_statistics(true); + + StringDedupTable::print_statistics(); + StringDedupQueue::print_statistics(); + } +} diff --git a/src/hotspot/share/gc/g1/g1StringDedupThread.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp similarity index 62% rename from src/hotspot/share/gc/g1/g1StringDedupThread.hpp rename to src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp index 651112519c4..a36cca60947 100644 --- a/src/hotspot/share/gc/g1/g1StringDedupThread.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -22,11 +22,11 @@ * */ -#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP -#define SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP -#include "gc/g1/g1StringDedupStat.hpp" #include "gc/shared/concurrentGCThread.hpp" +#include "gc/shared/stringdedup/stringDedupStat.hpp" // // The deduplication thread is where the actual deduplication occurs. It waits for @@ -36,25 +36,37 @@ // concurrently with the Java application but participates in safepoints to allow // the GC to adjust and unlink oops from the deduplication queue and table. // -class G1StringDedupThread: public ConcurrentGCThread { -private: - static G1StringDedupThread* _thread; +class StringDedupThread: public ConcurrentGCThread { +protected: + static StringDedupThread* _thread; - G1StringDedupThread(); - ~G1StringDedupThread(); + StringDedupThread(); + ~StringDedupThread(); - void print_start(const G1StringDedupStat& last_stat); - void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat); + void print_start(const StringDedupStat* last_stat); + void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat); - void run_service(); + void run_service() { this->do_deduplication(); } void stop_service(); + void deduplicate_shared_strings(StringDedupStat* stat); +protected: + virtual void do_deduplication() = 0; + +public: + static StringDedupThread* thread(); +}; + +template +class StringDedupThreadImpl : public StringDedupThread { +private: + StringDedupThreadImpl() { } + +protected: + void do_deduplication(); + public: static void create(); - - static G1StringDedupThread* thread(); - - void deduplicate_shared_strings(G1StringDedupStat& stat); }; -#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.inline.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.inline.hpp new file mode 100644 index 00000000000..74a53974cce --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.inline.hpp @@ -0,0 +1,91 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP +#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP + +#include "gc/shared/suspendibleThreadSet.hpp" +#include "gc/shared/stringdedup/stringDedupQueue.inline.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" + +template +void StringDedupThreadImpl::do_deduplication() { + S total_stat; + + deduplicate_shared_strings(&total_stat); + + // Main loop + for (;;) { + S stat; + + stat.mark_idle(); + + // Wait for the queue to become non-empty + StringDedupQueue::wait(); + if (this->should_terminate()) { + break; + } + + { + // Include thread in safepoints + SuspendibleThreadSetJoiner sts_join; + + stat.mark_exec(); + StringDedupStat::print_start(&stat); + + // Process the queue + for (;;) { + oop java_string = StringDedupQueue::pop(); + if (java_string == NULL) { + break; + } + + StringDedupTable::deduplicate(java_string, &stat); + + // Safepoint this thread if needed + if (sts_join.should_yield()) { + stat.mark_block(); + sts_join.yield(); + stat.mark_unblock(); + } + } + + stat.mark_done(); + + total_stat.add(&stat); + print_end(&stat, &total_stat); + stat.reset(); + } + + StringDedupTable::clean_entry_cache(); + } +} + +template +void StringDedupThreadImpl::create() { + assert(_thread == NULL, "One string deduplication thread allowed"); + _thread = new StringDedupThreadImpl(); +} + +#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 55ccfc1b008..891cc1da7ad 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -45,6 +45,14 @@ void ThreadLocalAllocBuffer::clear_before_allocation() { make_parsable(true); // also retire the TLAB } +size_t ThreadLocalAllocBuffer::remaining() { + if (end() == NULL) { + return 0; + } + + return pointer_delta(hard_end(), top()); +} + void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { global_stats()->initialize(); @@ -114,17 +122,19 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) { myThread()->incr_allocated_bytes(used_bytes()); } - CollectedHeap::fill_with_object(top(), hard_end(), retire && zap); + Universe::heap()->fill_with_dummy_object(top(), hard_end(), retire && zap); if (retire || ZeroTLAB) { // "Reset" the TLAB set_start(NULL); set_top(NULL); set_pf_top(NULL); set_end(NULL); + set_allocation_end(NULL); } } assert(!(retire || ZeroTLAB) || - (start() == NULL && end() == NULL && top() == NULL), + (start() == NULL && end() == NULL && top() == NULL && + _allocation_end == NULL), "TLAB must be reset"); } @@ -172,8 +182,13 @@ void ThreadLocalAllocBuffer::fill(HeapWord* start, _allocated_size += new_size; print_stats("fill"); assert(top <= start + new_size - alignment_reserve(), "size too small"); + initialize(start, top, start + new_size - alignment_reserve()); + if (ThreadHeapSampler::enabled()) { + set_sample_end(); + } + // Reset amount of internal fragmentation set_refill_waste_limit(initial_refill_waste_limit()); } @@ -185,6 +200,7 @@ void ThreadLocalAllocBuffer::initialize(HeapWord* start, set_top(top); set_pf_top(top); set_end(end); + set_allocation_end(end); invariants(); } @@ -306,12 +322,45 @@ void ThreadLocalAllocBuffer::verify() { guarantee(p == top(), "end of last object must match end of space"); } +void ThreadLocalAllocBuffer::set_sample_end() { + size_t heap_words_remaining = pointer_delta(_end, _top); + size_t bytes_until_sample = myThread()->heap_sampler().bytes_until_sample(); + size_t words_until_sample = bytes_until_sample / HeapWordSize;; + + if (heap_words_remaining > words_until_sample) { + HeapWord* new_end = _top + words_until_sample; + set_end(new_end); + _bytes_since_last_sample_point = bytes_until_sample; + } else { + _bytes_since_last_sample_point = heap_words_remaining * HeapWordSize;; + } +} + Thread* ThreadLocalAllocBuffer::myThread() { return (Thread*)(((char *)this) + in_bytes(start_offset()) - in_bytes(Thread::tlab_start_offset())); } +void ThreadLocalAllocBuffer::set_back_allocation_end() { + _end = _allocation_end; +} + +HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) { + set_back_allocation_end(); + HeapWord* result = allocate(size); + + if (result) { + myThread()->heap_sampler().check_for_sampling(result, size * HeapWordSize, _bytes_since_last_sample_point); + set_sample_end(); + } + + return result; +} + +HeapWord* ThreadLocalAllocBuffer::hard_end() { + return _allocation_end + alignment_reserve(); +} GlobalTLABStats::GlobalTLABStats() : _allocating_threads_avg(TLABAllocationWeight) { diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 1c8e2b22fed..657d4c4e17a 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -37,6 +37,12 @@ class GlobalTLABStats; // It is thread-private at any time, but maybe multiplexed over // time across multiple threads. The park()/unpark() pair is // used to make it available for such multiplexing. +// +// Heap sampling is performed via the end and allocation_end +// fields. +// allocation_end contains the real end of the tlab allocation, +// whereas end can be set to an arbitrary spot in the tlab to +// trip the return and sample the allocation. class ThreadLocalAllocBuffer: public CHeapObj { friend class VMStructs; friend class JVMCIVMStructs; @@ -44,10 +50,13 @@ private: HeapWord* _start; // address of TLAB HeapWord* _top; // address after last allocation HeapWord* _pf_top; // allocation prefetch watermark - HeapWord* _end; // allocation end (excluding alignment_reserve) + HeapWord* _end; // allocation end (can be the sampling end point or _allocation_end) + HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve) + size_t _desired_size; // desired size (including alignment_reserve) size_t _refill_waste_limit; // hold onto tlab if free() is larger than this size_t _allocated_before_last_gc; // total bytes allocated up until the last gc + size_t _bytes_since_last_sample_point; // bytes since last sample point. static size_t _max_size; // maximum size of any TLAB static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB @@ -67,6 +76,7 @@ private: void set_start(HeapWord* start) { _start = start; } void set_end(HeapWord* end) { _end = end; } + void set_allocation_end(HeapWord* ptr) { _allocation_end = ptr; } void set_top(HeapWord* top) { _top = top; } void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; } void set_desired_size(size_t desired_size) { _desired_size = desired_size; } @@ -77,7 +87,7 @@ private: static int target_refills() { return _target_refills; } size_t initial_desired_size(); - size_t remaining() const { return end() == NULL ? 0 : pointer_delta(hard_end(), top()); } + size_t remaining(); bool is_last_allocation(HeapWord* obj, size_t size) { return pointer_delta(top(), obj) == size; } @@ -118,8 +128,8 @@ public: HeapWord* start() const { return _start; } HeapWord* end() const { return _end; } - HeapWord* hard_end() const { return _end + alignment_reserve(); } HeapWord* top() const { return _top; } + HeapWord* hard_end(); HeapWord* pf_top() const { return _pf_top; } size_t desired_size() const { return _desired_size; } size_t used() const { return pointer_delta(top(), start()); } @@ -127,9 +137,11 @@ public: size_t free() const { return pointer_delta(end(), top()); } // Don't discard tlab if remaining space is larger than this. size_t refill_waste_limit() const { return _refill_waste_limit; } + size_t bytes_since_last_sample_point() const { return _bytes_since_last_sample_point; } // Allocate size HeapWords. The memory is NOT initialized to zero. inline HeapWord* allocate(size_t size); + HeapWord* allocate_sampled_object(size_t size); // Undo last allocation. inline bool undo_allocate(HeapWord* obj, size_t size); @@ -171,6 +183,9 @@ public: void fill(HeapWord* start, HeapWord* top, size_t new_size); void initialize(); + void set_back_allocation_end(); + void set_sample_end(); + static size_t refill_waste_limit_increment() { return TLABWasteIncrement; } template void addresses_do(T f) { @@ -178,6 +193,7 @@ public: f(&_top); f(&_pf_top); f(&_end); + f(&_allocation_end); } // Code generation support diff --git a/src/hotspot/share/gc/z/zArguments.cpp b/src/hotspot/share/gc/z/zArguments.cpp index 19fd2898e20..76aa414b3d2 100644 --- a/src/hotspot/share/gc/z/zArguments.cpp +++ b/src/hotspot/share/gc/z/zArguments.cpp @@ -94,11 +94,6 @@ void ZArguments::initialize() { // Verification of stacks not (yet) supported, for the same reason // we need fixup_partial_loads DEBUG_ONLY(FLAG_SET_DEFAULT(VerifyStack, false)); - - // JVMCI not (yet) supported - if (EnableJVMCI) { - vm_exit_during_initialization("The flag -XX:+UseZGC can not be combined with -XX:+EnableJVMCI"); - } } CollectedHeap* ZArguments::create_heap() { diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index 33375aff47b..4c993b2df16 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -45,7 +45,6 @@ ZBarrierSetAssembler* ZBarrierSet::assembler() { bool ZBarrierSet::barrier_needed(DecoratorSet decorators, BasicType type) { assert((decorators & AS_RAW) == 0, "Unexpected decorator"); assert((decorators & AS_NO_KEEPALIVE) == 0, "Unexpected decorator"); - assert((decorators & IN_ARCHIVE_ROOT) == 0, "Unexpected decorator"); //assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Unexpected decorator"); if (type == T_OBJECT || type == T_ARRAY) { diff --git a/src/hotspot/share/gc/z/zDriver.cpp b/src/hotspot/share/gc/z/zDriver.cpp index fdefad97187..2f128217d19 100644 --- a/src/hotspot/share/gc/z/zDriver.cpp +++ b/src/hotspot/share/gc/z/zDriver.cpp @@ -126,6 +126,47 @@ public: } }; +static bool should_clear_soft_references() { + // Clear if one or more allocations have stalled + const bool stalled = ZHeap::heap()->is_alloc_stalled(); + if (stalled) { + // Clear + return true; + } + + // Clear if implied by the GC cause + const GCCause::Cause cause = ZCollectedHeap::heap()->gc_cause(); + if (cause == GCCause::_wb_full_gc || + cause == GCCause::_metadata_GC_clear_soft_refs) { + // Clear + return true; + } + + // Don't clear + return false; +} + +static bool should_boost_worker_threads() { + // Boost worker threads if one or more allocations have stalled + const bool stalled = ZHeap::heap()->is_alloc_stalled(); + if (stalled) { + // Boost + return true; + } + + // Boost worker threads if implied by the GC cause + const GCCause::Cause cause = ZCollectedHeap::heap()->gc_cause(); + if (cause == GCCause::_wb_full_gc || + cause == GCCause::_java_lang_system_gc || + cause == GCCause::_metadata_GC_clear_soft_refs) { + // Boost + return true; + } + + // Don't boost + return false; +} + class ZMarkStartClosure : public ZOperationClosure { public: virtual const char* name() const { @@ -140,6 +181,14 @@ public: ZStatTimer timer(ZPhasePauseMarkStart); ZServiceabilityMarkStartTracer tracer; + // Setup soft reference policy + const bool clear = should_clear_soft_references(); + ZHeap::heap()->set_soft_reference_policy(clear); + + // Setup boost mode + const bool boost = should_boost_worker_threads(); + ZHeap::heap()->set_boost_worker_threads(boost); + ZCollectedHeap::heap()->increment_total_collections(true /* full */); ZHeap::heap()->mark_start(); @@ -247,58 +296,19 @@ GCCause::Cause ZDriver::start_gc_cycle() { return _gc_cycle_port.receive(); } -class ZSoftReferencePolicyScope : public StackObj { -private: - bool should_clear_soft_reference(GCCause::Cause cause) const { - const bool clear = ZCollectedHeap::heap()->soft_ref_policy()->should_clear_all_soft_refs(); - - // Clear all soft reference if the policy says so, or if - // the GC cause indicates that we're running low on memory. - return clear || - cause == GCCause::_z_allocation_stall || - cause == GCCause::_metadata_GC_clear_soft_refs; - } - - void clear_should_clear_soft_reference() const { - ZCollectedHeap::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(false); - } - -public: - ZSoftReferencePolicyScope(GCCause::Cause cause) { - const bool clear = should_clear_soft_reference(cause); - ZHeap::heap()->set_soft_reference_policy(clear); - clear_should_clear_soft_reference(); - } - - ~ZSoftReferencePolicyScope() { - Universe::update_heap_info_at_gc(); - } -}; - class ZDriverCycleScope : public StackObj { private: - GCIdMark _gc_id; - GCCauseSetter _gc_cause_setter; - ZSoftReferencePolicyScope _soft_ref_policy; - ZStatTimer _timer; - - bool should_boost_worker_threads(GCCause::Cause cause) const { - return cause == GCCause::_java_lang_system_gc || - cause == GCCause::_z_allocation_stall; - } + GCIdMark _gc_id; + GCCauseSetter _gc_cause_setter; + ZStatTimer _timer; public: ZDriverCycleScope(GCCause::Cause cause) : _gc_id(), _gc_cause_setter(ZCollectedHeap::heap(), cause), - _soft_ref_policy(cause), _timer(ZPhaseCycle) { // Update statistics ZStatCycle::at_start(); - - // Set boost mode - const bool boost = should_boost_worker_threads(cause); - ZHeap::heap()->set_boost_worker_threads(boost); } ~ZDriverCycleScope() { @@ -308,6 +318,9 @@ public: // Update statistics ZStatCycle::at_end(boost_factor); + + // Update data used by soft reference policy + Universe::update_heap_info_at_gc(); } }; diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 58cde9e869a..76901f9b264 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -504,7 +504,6 @@ void ZHeap::relocate() { } void ZHeap::object_iterate(ObjectClosure* cl) { - // Should only be called in a safepoint after mark end. assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); ZHeapIterator iter; diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index 575d753dd93..720e580d760 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -124,6 +124,7 @@ public: uintptr_t alloc_object(size_t size); uintptr_t alloc_object_for_relocation(size_t size); void undo_alloc_object_for_relocation(uintptr_t addr, size_t size); + bool is_alloc_stalled() const; void check_out_of_memory(); // Marking diff --git a/src/hotspot/share/gc/z/zHeap.inline.hpp b/src/hotspot/share/gc/z/zHeap.inline.hpp index 1e6beb2bf00..0af2f9f6ac9 100644 --- a/src/hotspot/share/gc/z/zHeap.inline.hpp +++ b/src/hotspot/share/gc/z/zHeap.inline.hpp @@ -89,6 +89,10 @@ inline void ZHeap::undo_alloc_object_for_relocation(uintptr_t addr, size_t size) _object_allocator.undo_alloc_object_for_relocation(page, addr, size); } +inline bool ZHeap::is_alloc_stalled() const { + return _page_allocator.is_alloc_stalled(); +} + inline void ZHeap::check_out_of_memory() { _page_allocator.check_out_of_memory(); } diff --git a/src/hotspot/share/gc/z/zHeapIterator.cpp b/src/hotspot/share/gc/z/zHeapIterator.cpp index 03c4e16cfbb..4c5bde58c36 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.cpp +++ b/src/hotspot/share/gc/z/zHeapIterator.cpp @@ -63,7 +63,7 @@ public: virtual void do_oop(oop* p) { // Load barrier needed here for the same reason we // need fixup_partial_loads() in ZHeap::mark_end() - const oop obj = RootAccess<>::oop_load(p); + const oop obj = ZBarrier::load_barrier_on_oop_field(p); _iter->push(obj); _iter->drain(_cl); } diff --git a/src/hotspot/share/gc/z/zMessagePort.inline.hpp b/src/hotspot/share/gc/z/zMessagePort.inline.hpp index dce6f22fa18..897a836ebc4 100644 --- a/src/hotspot/share/gc/z/zMessagePort.inline.hpp +++ b/src/hotspot/share/gc/z/zMessagePort.inline.hpp @@ -87,6 +87,17 @@ inline void ZMessagePort::send_sync(T message) { // Wait for completion request.wait(); + + { + // Guard deletion of underlying semaphore. This is a workaround for a + // bug in sem_post() in glibc < 2.21, where it's not safe to destroy + // the semaphore immediately after returning from sem_wait(). The + // reason is that sem_post() can touch the semaphore after a waiting + // thread have returned from sem_wait(). To avoid this race we are + // forcing the waiting thread to acquire/release the lock held by the + // posting thread. https://sourceware.org/bugzilla/show_bug.cgi?id=12674 + MonitorLockerEx ml(&_monitor, Monitor::_no_safepoint_check_flag); + } } template diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index e77e409d3f7..53e761d2f4e 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -337,6 +337,17 @@ ZPage* ZPageAllocator::alloc_page_blocking(uint8_t type, size_t size, ZAllocatio // Wait for allocation to complete or fail page = request.wait(); } while (page == gc_marker); + + { + // Guard deletion of underlying semaphore. This is a workaround for a + // bug in sem_post() in glibc < 2.21, where it's not safe to destroy + // the semaphore immediately after returning from sem_wait(). The + // reason is that sem_post() can touch the semaphore after a waiting + // thread have returned from sem_wait(). To avoid this race we are + // forcing the waiting thread to acquire/release the lock held by the + // posting thread. https://sourceware.org/bugzilla/show_bug.cgi?id=12674 + ZLocker locker(&_lock); + } } return page; @@ -448,24 +459,25 @@ void ZPageAllocator::free_page(ZPage* page, bool reclaimed) { satisfy_alloc_queue(); } +bool ZPageAllocator::is_alloc_stalled() const { + assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); + return !_queue.is_empty(); +} + void ZPageAllocator::check_out_of_memory() { ZLocker locker(&_lock); - ZPageAllocRequest* const first = _queue.first(); - if (first == NULL) { - // Allocation queue is empty - return; - } - - // Fail the allocation request if it was enqueued before the + // Fail allocation requests that were enqueued before the // last GC cycle started, otherwise start a new GC cycle. - if (first->total_collections() < ZCollectedHeap::heap()->total_collections()) { - // Out of memory, fail all enqueued requests - for (ZPageAllocRequest* request = _queue.remove_first(); request != NULL; request = _queue.remove_first()) { - request->satisfy(NULL); + for (ZPageAllocRequest* request = _queue.first(); request != NULL; request = _queue.first()) { + if (request->total_collections() == ZCollectedHeap::heap()->total_collections()) { + // Start a new GC cycle, keep allocation requests enqueued + request->satisfy(gc_marker); + return; } - } else { - // Start another GC cycle, keep all enqueued requests - first->satisfy(gc_marker); + + // Out of memory, fail allocation request + _queue.remove_first(); + request->satisfy(NULL); } } diff --git a/src/hotspot/share/gc/z/zPageAllocator.hpp b/src/hotspot/share/gc/z/zPageAllocator.hpp index 6bd6465f386..dc2578a7fca 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.hpp +++ b/src/hotspot/share/gc/z/zPageAllocator.hpp @@ -102,6 +102,7 @@ public: void flip_pre_mapped(); + bool is_alloc_stalled() const; void check_out_of_memory(); }; diff --git a/src/hotspot/share/gc/z/zRootsIterator.cpp b/src/hotspot/share/gc/z/zRootsIterator.cpp index 107899ed686..4cff2cca6b5 100644 --- a/src/hotspot/share/gc/z/zRootsIterator.cpp +++ b/src/hotspot/share/gc/z/zRootsIterator.cpp @@ -307,10 +307,12 @@ ZWeakRootsIterator::ZWeakRootsIterator() : assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); ZStatTimer timer(ZSubPhasePauseWeakRootsSetup); SymbolTable::clear_parallel_claimed_index(); + StringTable::reset_dead_counter(); } ZWeakRootsIterator::~ZWeakRootsIterator() { ZStatTimer timer(ZSubPhasePauseWeakRootsTeardown); + StringTable::finish_dead_counter(); } void ZWeakRootsIterator::do_vm_weak_handles(BoolObjectClosure* is_alive, OopClosure* cl) { @@ -341,9 +343,34 @@ void ZWeakRootsIterator::do_symbol_table(BoolObjectClosure* is_alive, OopClosure SymbolTable::possibly_parallel_unlink(&dummy, &dummy); } +class ZStringTableDeadCounterBoolObjectClosure : public BoolObjectClosure { +private: + BoolObjectClosure* const _cl; + size_t _ndead; + +public: + ZStringTableDeadCounterBoolObjectClosure(BoolObjectClosure* cl) : + _cl(cl), + _ndead(0) {} + + ~ZStringTableDeadCounterBoolObjectClosure() { + StringTable::inc_dead_counter(_ndead); + } + + virtual bool do_object_b(oop obj) { + if (_cl->do_object_b(obj)) { + return true; + } + + _ndead++; + return false; + } +}; + void ZWeakRootsIterator::do_string_table(BoolObjectClosure* is_alive, OopClosure* cl) { ZStatTimer timer(ZSubPhasePauseWeakRootsStringTable); - _string_table_iter.weak_oops_do(is_alive, cl); + ZStringTableDeadCounterBoolObjectClosure counter_is_alive(is_alive); + _string_table_iter.weak_oops_do(&counter_is_alive, cl); } void ZWeakRootsIterator::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* cl) { @@ -377,7 +404,13 @@ ZConcurrentWeakRootsIterator::ZConcurrentWeakRootsIterator() : _string_table_iter(StringTable::weak_storage()), _vm_weak_handles(this), _jni_weak_handles(this), - _string_table(this) {} + _string_table(this) { + StringTable::reset_dead_counter(); +} + +ZConcurrentWeakRootsIterator::~ZConcurrentWeakRootsIterator() { + StringTable::finish_dead_counter(); +} void ZConcurrentWeakRootsIterator::do_vm_weak_handles(OopClosure* cl) { ZStatTimer timer(ZSubPhaseConcurrentWeakRootsVMWeakHandles); @@ -389,9 +422,36 @@ void ZConcurrentWeakRootsIterator::do_jni_weak_handles(OopClosure* cl) { _jni_weak_handles_iter.oops_do(cl); } +class ZStringTableDeadCounterOopClosure : public OopClosure { +private: + OopClosure* const _cl; + size_t _ndead; + +public: + ZStringTableDeadCounterOopClosure(OopClosure* cl) : + _cl(cl), + _ndead(0) {} + + ~ZStringTableDeadCounterOopClosure() { + StringTable::inc_dead_counter(_ndead); + } + + virtual void do_oop(oop* p) { + _cl->do_oop(p); + if (*p == NULL) { + _ndead++; + } + } + + virtual void do_oop(narrowOop* p) { + ShouldNotReachHere(); + } +}; + void ZConcurrentWeakRootsIterator::do_string_table(OopClosure* cl) { ZStatTimer timer(ZSubPhaseConcurrentWeakRootsStringTable); - _string_table_iter.oops_do(cl); + ZStringTableDeadCounterOopClosure counter_cl(cl); + _string_table_iter.oops_do(&counter_cl); } void ZConcurrentWeakRootsIterator::oops_do(OopClosure* cl) { diff --git a/src/hotspot/share/gc/z/zRootsIterator.hpp b/src/hotspot/share/gc/z/zRootsIterator.hpp index f723d9195f3..f3bc1b8ab9d 100644 --- a/src/hotspot/share/gc/z/zRootsIterator.hpp +++ b/src/hotspot/share/gc/z/zRootsIterator.hpp @@ -164,6 +164,7 @@ private: public: ZConcurrentWeakRootsIterator(); + ~ZConcurrentWeakRootsIterator(); void oops_do(OopClosure* cl); }; diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index 76bcc2758dd..a90e5054fa0 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "jvm.h" #include "jvmci/jvmci_globals.hpp" +#include "gc/shared/gcConfig.hpp" #include "utilities/defaultStream.hpp" #include "runtime/globals_extension.hpp" @@ -113,3 +114,13 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() { #undef CHECK_NOT_SET return true; } +void JVMCIGlobals::check_jvmci_supported_gc() { + if (EnableJVMCI) { + // Check if selected GC is supported by JVMCI and Java compiler + if (!(UseSerialGC || UseParallelGC || UseParallelOldGC || UseG1GC)) { + vm_exit_during_initialization("JVMCI Compiler does not support selected GC", GCConfig::hs_err_name()); + FLAG_SET_DEFAULT(EnableJVMCI, false); + FLAG_SET_DEFAULT(UseJVMCICompiler, false); + } + } +} diff --git a/src/hotspot/share/jvmci/jvmci_globals.hpp b/src/hotspot/share/jvmci/jvmci_globals.hpp index 0944494eb69..a88ed845df3 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.hpp +++ b/src/hotspot/share/jvmci/jvmci_globals.hpp @@ -121,5 +121,8 @@ class JVMCIGlobals { // an error message describing the inconsistency is printed before // returning false. static bool check_jvmci_flags_are_consistent(); + + // Check and exit VM with error if selected GC is not supported by JVMCI. + static void check_jvmci_supported_gc(); }; #endif // SHARE_VM_JVMCI_JVMCIGLOBALS_HPP diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 307c89c217c..c72ea682a70 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -136,6 +136,7 @@ nonstatic_field(DataLayout, _header._struct._tag, u1) \ nonstatic_field(DataLayout, _header._struct._flags, u1) \ nonstatic_field(DataLayout, _header._struct._bci, u2) \ + nonstatic_field(DataLayout, _header._struct._traps, u4) \ nonstatic_field(DataLayout, _cells[0], intptr_t) \ \ nonstatic_field(Deoptimization::UnrollBlock, _size_of_deoptimized_frame, int) \ diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index 8571f8a31a3..12c6ff0bbf3 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -30,6 +30,7 @@ #include "memory/heapInspection.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" +#include "oops/reflectionAccessorImplKlassHelper.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -490,6 +491,12 @@ void KlassHierarchy::print_class(outputStream* st, KlassInfoEntry* cie, bool pri if (klass->is_interface()) { st->print(" (intf)"); } + // Special treatment for generated core reflection accessor classes: print invocation target. + if (ReflectionAccessorImplKlassHelper::is_generated_accessor(klass)) { + st->print(" (invokes: "); + ReflectionAccessorImplKlassHelper::print_invocation_target(st, klass); + st->print(")"); + } st->print("\n"); // Print any interfaces the class has. diff --git a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp index 4276988b472..983e28b4592 100644 --- a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp +++ b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.cpp @@ -24,13 +24,9 @@ */ #include "precompiled.hpp" -#include "classfile/systemDictionary.hpp" #include "memory/metaspace/printMetaspaceInfoKlassClosure.hpp" #include "memory/resourceArea.hpp" -#include "oops/constantPool.inline.hpp" -#include "oops/instanceKlass.hpp" -#include "oops/klass.hpp" -#include "utilities/constantTag.hpp" +#include "oops/reflectionAccessorImplKlassHelper.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -54,6 +50,14 @@ void PrintMetaspaceInfoKlassClosure::do_klass(Klass* k) { _out->print(UINTX_FORMAT_W(4) ": ", _num_classes); ResourceMark rm; _out->print("%s", k->external_name()); + + // Special treatment for generated core reflection accessor classes: print invocation target. + if (ReflectionAccessorImplKlassHelper::is_generated_accessor(k)) { + _out->print(" (invokes: "); + ReflectionAccessorImplKlassHelper::print_invocation_target(_out, k); + _out->print(")"); + } + } } diff --git a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.hpp b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.hpp index a0413c75e2d..3bdb51e6df7 100644 --- a/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.hpp +++ b/src/hotspot/share/memory/metaspace/printMetaspaceInfoKlassClosure.hpp @@ -40,7 +40,10 @@ private: outputStream* const _out; const bool _do_print; + bool print_reflection_invocation_target(outputStream* out, InstanceKlass* magic_accessor_impl_class); + public: + uintx _num_classes; uintx _num_instance_classes; uintx _num_array_classes; diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index a89cfd0d25f..08b3546f3d3 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -1910,6 +1910,11 @@ oop MetaspaceShared::archive_heap_object(oop obj, Thread* THREAD) { return archived_oop; } +oop MetaspaceShared::materialize_archived_object(oop obj) { + assert(obj != NULL, "sanity"); + return G1CollectedHeap::heap()->materialize_archived_object(obj); +} + void MetaspaceShared::archive_klass_objects(Thread* THREAD) { int i; for (i = 0; i < _global_klass_objects->length(); i++) { @@ -1980,7 +1985,7 @@ public: "Archived heap object is not allowed"); assert(MetaspaceShared::open_archive_heap_region_mapped(), "Open archive heap region is not mapped"); - RootAccess::oop_store(p, CompressedOops::decode_not_null(o)); + *p = CompressedOops::decode_not_null(o); } } diff --git a/src/hotspot/share/memory/metaspaceShared.hpp b/src/hotspot/share/memory/metaspaceShared.hpp index d54d4785697..c0eaaf8386f 100644 --- a/src/hotspot/share/memory/metaspaceShared.hpp +++ b/src/hotspot/share/memory/metaspaceShared.hpp @@ -111,6 +111,7 @@ class MetaspaceShared : AllStatic { } static oop find_archived_heap_object(oop obj); static oop archive_heap_object(oop obj, Thread* THREAD); + static oop materialize_archived_object(oop obj); static void archive_klass_objects(Thread* THREAD); #endif diff --git a/src/hotspot/share/oops/access.hpp b/src/hotspot/share/oops/access.hpp index 6e9ec4f08de..4728f402236 100644 --- a/src/hotspot/share/oops/access.hpp +++ b/src/hotspot/share/oops/access.hpp @@ -379,8 +379,7 @@ void Access::verify_decorators() { (location_decorators ^ IN_ROOT) == 0 || (location_decorators ^ IN_HEAP) == 0 || (location_decorators ^ (IN_HEAP | IN_HEAP_ARRAY)) == 0 || - (location_decorators ^ (IN_ROOT | IN_CONCURRENT_ROOT)) == 0 || - (location_decorators ^ (IN_ROOT | IN_ARCHIVE_ROOT)) == 0 + (location_decorators ^ (IN_ROOT | IN_CONCURRENT_ROOT)) == 0 )); } diff --git a/src/hotspot/share/oops/accessDecorators.hpp b/src/hotspot/share/oops/accessDecorators.hpp index 3b4e7aa9ede..723e6136b52 100644 --- a/src/hotspot/share/oops/accessDecorators.hpp +++ b/src/hotspot/share/oops/accessDecorators.hpp @@ -192,10 +192,8 @@ const DecoratorSet IN_HEAP = UCONST64(1) << 20; const DecoratorSet IN_HEAP_ARRAY = UCONST64(1) << 21; const DecoratorSet IN_ROOT = UCONST64(1) << 22; const DecoratorSet IN_CONCURRENT_ROOT = UCONST64(1) << 23; -const DecoratorSet IN_ARCHIVE_ROOT = UCONST64(1) << 24; const DecoratorSet IN_DECORATOR_MASK = IN_HEAP | IN_HEAP_ARRAY | - IN_ROOT | IN_CONCURRENT_ROOT | - IN_ARCHIVE_ROOT; + IN_ROOT | IN_CONCURRENT_ROOT; // == Value Decorators == // * OOP_NOT_NULL: This property can make certain barriers faster such as compressing oops. @@ -245,9 +243,7 @@ namespace AccessInternal { ((IN_HEAP_ARRAY & barrier_strength_default) != 0 ? IN_HEAP : INTERNAL_EMPTY); static const DecoratorSet conc_root_is_root = heap_array_is_in_heap | ((IN_CONCURRENT_ROOT & heap_array_is_in_heap) != 0 ? IN_ROOT : INTERNAL_EMPTY); - static const DecoratorSet archive_root_is_root = conc_root_is_root | - ((IN_ARCHIVE_ROOT & conc_root_is_root) != 0 ? IN_ROOT : INTERNAL_EMPTY); - static const DecoratorSet value = archive_root_is_root | BT_BUILDTIME_DECORATORS; + static const DecoratorSet value = conc_root_is_root | BT_BUILDTIME_DECORATORS; }; // This function implements the above DecoratorFixup rules, but without meta @@ -268,9 +264,7 @@ namespace AccessInternal { ((IN_HEAP_ARRAY & barrier_strength_default) != 0 ? IN_HEAP : INTERNAL_EMPTY); DecoratorSet conc_root_is_root = heap_array_is_in_heap | ((IN_CONCURRENT_ROOT & heap_array_is_in_heap) != 0 ? IN_ROOT : INTERNAL_EMPTY); - DecoratorSet archive_root_is_root = conc_root_is_root | - ((IN_ARCHIVE_ROOT & conc_root_is_root) != 0 ? IN_ROOT : INTERNAL_EMPTY); - DecoratorSet value = archive_root_is_root | BT_BUILDTIME_DECORATORS; + DecoratorSet value = conc_root_is_root | BT_BUILDTIME_DECORATORS; return value; } } diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index d8ebf86cdda..6f73a80bdb1 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -32,6 +32,7 @@ #include "logging/log.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/access.inline.hpp" @@ -743,16 +744,15 @@ void ConstantPoolCache::deallocate_contents(ClassLoaderData* data) { #if INCLUDE_CDS_JAVA_HEAP oop ConstantPoolCache::archived_references() { - // Loading an archive root forces the oop to become strongly reachable. - // For example, if it is loaded during concurrent marking in a SATB - // collector, it will be enqueued to the SATB queue, effectively - // shading the previously white object gray. - return RootAccess::oop_load(&_archived_references); + if (CompressedOops::is_null(_archived_references)) { + return NULL; + } + return MetaspaceShared::materialize_archived_object(CompressedOops::decode_not_null(_archived_references)); } void ConstantPoolCache::set_archived_references(oop o) { assert(DumpSharedSpaces, "called only during runtime"); - RootAccess::oop_store(&_archived_references, o); + _archived_references = CompressedOops::encode(o); } #endif diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 42a02d68299..6316ca206a6 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -529,20 +529,19 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec Handle module_handle(THREAD, ((module_entry != NULL) ? module_entry->module() : (oop)NULL)); if (this->has_raw_archived_mirror()) { + ResourceMark rm; log_debug(cds, mirror)("%s has raw archived mirror", external_name()); if (MetaspaceShared::open_archive_heap_region_mapped()) { - oop m = archived_java_mirror(); - log_debug(cds, mirror)("Archived mirror is: " PTR_FORMAT, p2i(m)); - if (m != NULL) { - // mirror is archived, restore - assert(MetaspaceShared::is_archive_object(m), "must be archived mirror object"); - Handle m_h(THREAD, m); - java_lang_Class::restore_archived_mirror(this, m_h, loader, module_handle, protection_domain, CHECK); + bool present = java_lang_Class::restore_archived_mirror(this, loader, module_handle, + protection_domain, + CHECK); + if (present) { return; } } // No archived mirror data + log_debug(cds, mirror)("No archived mirror data for %s", external_name()); _java_mirror = NULL; this->clear_has_raw_archived_mirror(); } @@ -558,18 +557,10 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec #if INCLUDE_CDS_JAVA_HEAP // Used at CDS dump time to access the archived mirror. No GC barrier. oop Klass::archived_java_mirror_raw() { - assert(DumpSharedSpaces, "called only during runtime"); assert(has_raw_archived_mirror(), "must have raw archived mirror"); return CompressedOops::decode(_archived_mirror); } -// Used at CDS runtime to get the archived mirror from shared class. Uses GC barrier. -oop Klass::archived_java_mirror() { - assert(UseSharedSpaces, "UseSharedSpaces expected."); - assert(has_raw_archived_mirror(), "must have raw archived mirror"); - return RootAccess::oop_load(&_archived_mirror); -} - // No GC barrier void Klass::set_archived_java_mirror_raw(oop m) { assert(DumpSharedSpaces, "called only during runtime"); @@ -818,10 +809,10 @@ const char* Klass::class_loader_and_module_name() const { module_name = module->name()->as_C_string(); msglen += strlen(module_name); // Use version if exists and is not a jdk module - if (module->is_non_jdk_module() && module->version() != NULL) { + if (module->should_show_version()) { has_version = true; version = module->version()->as_C_string(); - msglen += strlen("@") + strlen(version); + msglen += strlen(version) + 1; // +1 for "@" } } } else { diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 3c2893c045e..8fca68b0f26 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -244,7 +244,6 @@ protected: void set_java_mirror(Handle m); oop archived_java_mirror_raw() NOT_CDS_JAVA_HEAP_RETURN_(NULL); // no GC barrier - oop archived_java_mirror() NOT_CDS_JAVA_HEAP_RETURN_(NULL); // accessor with GC barrier void set_archived_java_mirror_raw(oop m) NOT_CDS_JAVA_HEAP_RETURN; // no GC barrier // Temporary mirror switch used by RedefineClasses diff --git a/src/hotspot/share/oops/methodCounters.hpp b/src/hotspot/share/oops/methodCounters.hpp index 1d526ca46d0..76e2ee36ea1 100644 --- a/src/hotspot/share/oops/methodCounters.hpp +++ b/src/hotspot/share/oops/methodCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -26,6 +26,7 @@ #define SHARE_VM_OOPS_METHODCOUNTERS_HPP #include "oops/metadata.hpp" +#include "compiler/compilerDefinitions.hpp" #include "compiler/compilerOracle.hpp" #include "interpreter/invocationCounter.hpp" #include "runtime/arguments.hpp" @@ -96,7 +97,7 @@ class MethodCounters : public Metadata { double scale = 1.0; CompilerOracle::has_option_value(mh, "CompileThresholdScaling", scale); - int compile_threshold = Arguments::scaled_compile_threshold(CompileThreshold, scale); + int compile_threshold = CompilerConfig::scaled_compile_threshold(CompileThreshold, scale); _interpreter_invocation_limit = compile_threshold << InvocationCounter::count_shift; if (ProfileInterpreter) { // If interpreter profiling is enabled, the backward branch limit @@ -107,8 +108,8 @@ class MethodCounters : public Metadata { _interpreter_backward_branch_limit = ((compile_threshold * OnStackReplacePercentage) / 100) << InvocationCounter::count_shift; } _interpreter_profile_limit = ((compile_threshold * InterpreterProfilePercentage) / 100) << InvocationCounter::count_shift; - _invoke_mask = right_n_bits(Arguments::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; - _backedge_mask = right_n_bits(Arguments::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _invoke_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _backedge_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; } public: diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 52b31451934..0be0f66b964 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -1217,8 +1217,8 @@ void MethodData::init() { // Set per-method invoke- and backedge mask. double scale = 1.0; CompilerOracle::has_option_value(_method, "CompileThresholdScaling", scale); - _invoke_mask = right_n_bits(Arguments::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; - _backedge_mask = right_n_bits(Arguments::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _invoke_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _backedge_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; _tenure_traps = 0; _num_loops = 0; diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index f38f858f18f..dd91d90c8c6 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -83,16 +83,17 @@ class DataLayout { private: // Every data layout begins with a header. This header // contains a tag, which is used to indicate the size/layout - // of the data, 4 bits of flags, which can be used in any way, - // 4 bits of trap history (none/one reason/many reasons), + // of the data, 8 bits of flags, which can be used in any way, + // 32 bits of trap history (none/one reason/many reasons), // and a bci, which is used to tie this piece of data to a // specific bci in the bytecodes. union { - intptr_t _bits; + u8 _bits; struct { u1 _tag; u1 _flags; u2 _bci; + u4 _traps; } _struct; } _header; @@ -131,28 +132,23 @@ public: }; enum { - // The _struct._flags word is formatted as [trap_state:4 | flags:4]. - // The trap state breaks down further as [recompile:1 | reason:3]. + // The trap state breaks down as [recompile:1 | reason:31]. // This further breakdown is defined in deoptimization.cpp. // See Deoptimization::trap_state_reason for an assert that // trap_bits is big enough to hold reasons < Reason_RECORDED_LIMIT. // // The trap_state is collected only if ProfileTraps is true. - trap_bits = 1+3, // 3: enough to distinguish [0..Reason_RECORDED_LIMIT]. - trap_shift = BitsPerByte - trap_bits, + trap_bits = 1+31, // 31: enough to distinguish [0..Reason_RECORDED_LIMIT]. trap_mask = right_n_bits(trap_bits), - trap_mask_in_place = (trap_mask << trap_shift), - flag_limit = trap_shift, - flag_mask = right_n_bits(flag_limit), first_flag = 0 }; // Size computation static int header_size_in_bytes() { - return cell_size; + return header_size_in_cells() * cell_size; } static int header_size_in_cells() { - return 1; + return LP64_ONLY(1) NOT_LP64(2); } static int compute_size_in_bytes(int cell_count) { @@ -167,7 +163,7 @@ public: return _header._struct._tag; } - // Return a few bits of trap state. Range is [0..trap_mask]. + // Return 32 bits of trap state. // The state tells if traps with zero, one, or many reasons have occurred. // It also tells whether zero or many recompilations have occurred. // The associated trap histogram in the MDO itself tells whether @@ -175,14 +171,14 @@ public: // occurred, and the MDO shows N occurrences of X, we make the // simplifying assumption that all N occurrences can be blamed // on that BCI. - int trap_state() const { - return ((_header._struct._flags >> trap_shift) & trap_mask); + uint trap_state() const { + return _header._struct._traps; } - void set_trap_state(int new_state) { + void set_trap_state(uint new_state) { assert(ProfileTraps, "used only under +ProfileTraps"); - uint old_flags = (_header._struct._flags & flag_mask); - _header._struct._flags = (new_state << trap_shift) | old_flags; + uint old_flags = _header._struct._traps; + _header._struct._traps = new_state | old_flags; } u1 flags() const { @@ -193,10 +189,10 @@ public: return _header._struct._bci; } - void set_header(intptr_t value) { + void set_header(u8 value) { _header._bits = value; } - intptr_t header() { + u8 header() { return _header._bits; } void set_cell_at(int index, intptr_t value) { @@ -207,12 +203,10 @@ public: return _cells[index]; } - void set_flag_at(int flag_number) { - assert(flag_number < flag_limit, "oob"); + void set_flag_at(u1 flag_number) { _header._struct._flags |= (0x1 << flag_number); } - bool flag_at(int flag_number) const { - assert(flag_number < flag_limit, "oob"); + bool flag_at(u1 flag_number) const { return (_header._struct._flags & (0x1 << flag_number)) != 0; } @@ -233,14 +227,13 @@ public: return byte_offset_of(DataLayout, _cells) + in_ByteSize(index * cell_size); } // Return a value which, when or-ed as a byte into _flags, sets the flag. - static int flag_number_to_byte_constant(int flag_number) { - assert(0 <= flag_number && flag_number < flag_limit, "oob"); + static u1 flag_number_to_constant(u1 flag_number) { DataLayout temp; temp.set_header(0); temp.set_flag_at(flag_number); return temp._header._struct._flags; } // Return a value which, when or-ed as a word into _header, sets the flag. - static intptr_t flag_mask_to_header_mask(int byte_constant) { + static u8 flag_mask_to_header_mask(uint byte_constant) { DataLayout temp; temp.set_header(0); temp._header._struct._flags = byte_constant; return temp._header._bits; @@ -359,8 +352,8 @@ protected: static ByteSize cell_offset(int index) { return DataLayout::cell_offset(index); } - static int flag_number_to_byte_constant(int flag_number) { - return DataLayout::flag_number_to_byte_constant(flag_number); + static int flag_number_to_constant(int flag_number) { + return DataLayout::flag_number_to_constant(flag_number); } ProfileData(DataLayout* data) { @@ -534,7 +527,7 @@ public: // Code generation support static int null_seen_byte_constant() { - return flag_number_to_byte_constant(null_seen_flag); + return flag_number_to_constant(null_seen_flag); } static ByteSize bit_data_size() { @@ -1862,6 +1855,17 @@ class SpeculativeTrapData : public ProfileData { protected: enum { speculative_trap_method, +#ifndef _LP64 + // The size of the area for traps is a multiple of the header + // size, 2 cells on 32 bits. Packed at the end of this area are + // argument info entries (with tag + // DataLayout::arg_info_data_tag). The logic in + // MethodData::bci_to_extra_data() that guarantees traps don't + // overflow over argument info entries assumes the size of a + // SpeculativeTrapData is twice the header size. On 32 bits, a + // SpeculativeTrapData must be 4 cells. + padding, +#endif speculative_trap_cell_count }; public: diff --git a/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.cpp b/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.cpp new file mode 100644 index 00000000000..8470f8b3a4c --- /dev/null +++ b/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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/systemDictionary.hpp" +#include "memory/resourceArea.hpp" +#include "oops/reflectionAccessorImplKlassHelper.hpp" +#include "utilities/constantTag.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +// This code extracts name of target class, method and signature from the constant pool of a class +// assumed to be of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX. +// Since this may be affected by bitrot if these classes change, extra care is taken to make the +// release build of this coding robust. + +// We extract target class name, method name and sig from the constant pool of the Accessor class. +// This is an excerpt of the Constant pool (see jdk/internal/reflect/MethodAccessorGenerator.java:) + +// (^ = Only present if generating SerializationConstructorAccessor) +// 1 [UTF-8] [This class's name] +// 2 [CONSTANT_Class_info] for above +// 3 [UTF-8] "jdk/internal/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}" +// 4 [CONSTANT_Class_info] for above +// 5 [UTF-8] [Target class's name] +// 6 [CONSTANT_Class_info] for above +// 7^ [UTF-8] [Serialization: Class's name in which to invoke constructor] +// 8^ [CONSTANT_Class_info] for above +// 9 [UTF-8] target method or constructor name +// 10 [UTF-8] target method or constructor signature + +// Note that these strings are found at slightly different slots depending on the class type: +// - MethodAccessorImpl, ConstructoreAccessorImpl: slots 5, 7 and 8. +// - SerializationConstructorAccessorImpl: slots 5, 9 and 10. +// Unfortunately SerializationConstructorAccessorImpl is a child of ConstructoreAccessorImpl and there +// is no easy way to tell them apart. So we examine parent class name. + +enum cpi_slots { + cpi_slot_parent_class_name = 3, + cpi_slot_target_class_name = 5, + cpi_slot_target_method_name = 7, + cpi_slot_target_method_name_sca = 9, // SerializationConstructorAccessor case, see above + cpi_slot_target_method_sig = 8, + cpi_slot_target_method_sig_sca = 10 // SerializationConstructorAccessor case, see above +}; + +// Returns a string, resource-area allocated, from an UTF8 slot in the constant pool in the +// given Klass*. +static const char* get_string_from_cp_with_checks(const InstanceKlass* k, int cpi) { + const char* s = NULL; + const ConstantPool* const cp = k->constants(); + + assert(cp != NULL, "No cp?"); + assert(cp->is_within_bounds(cpi), "Unexpected constant pool layout for \"%s\", child class of Generated{Method|Constructor}AccessorImplXXX" + " (cpi %d out of bounds for [0..%d)).", k->external_name(), cpi, cp->length()); + assert(cp->tag_at(cpi).is_utf8(), "Unexpected constant pool layout for \"%s\", child class of Generated{Method|Constructor}AccessorImplXXX" + " (no UTF8 at cpi %d (%u)).", k->external_name(), cpi, cp->tag_at(cpi).value()); + + // Be nice in release: lets not crash, just return NULL. + if (cp != NULL && cp->is_within_bounds(cpi) && cp->tag_at(cpi).is_utf8()) { + s = cp->symbol_at(cpi)->as_C_string(); + } + + return s; +} + +// helper, returns true if class name of given class matches a given prefix +static bool classname_matches_prefix(const Klass* k, const char* prefix) { + const char* classname = k->external_name(); + if (classname != NULL) { + if (::strncmp(classname, prefix, strlen(prefix)) == 0) { + return true; + } + } + return false; +} + +// Returns true if k is of type jdk/internal/reflect/GeneratedMethodAccessorXXX. +bool ReflectionAccessorImplKlassHelper::is_generated_method_accessor(const InstanceKlass* k) { + return k->super() == SystemDictionary::reflect_MethodAccessorImpl_klass() && + classname_matches_prefix(k, "jdk.internal.reflect.GeneratedMethodAccessor"); +} + +// Returns true if k is of type jdk/internal/reflect/GeneratedConstructorAccessorXXX. +bool ReflectionAccessorImplKlassHelper::is_generated_constructor_accessor(const InstanceKlass* k) { + return k->super() == SystemDictionary::reflect_ConstructorAccessorImpl_klass() && + classname_matches_prefix(k, "jdk.internal.reflect.GeneratedConstructorAccessor"); +} + +// Returns true if k is of type jdk/internal/reflect/GeneratedSerializationConstructorAccessorXXX. +bool ReflectionAccessorImplKlassHelper::is_generated_method_serialization_constructor_accessor(const InstanceKlass* k) { + // GeneratedSerializationConstructorAccessor is not a direct subclass of ConstructorAccessorImpl + const Klass* sk = k->super(); + if (sk != NULL && sk->super() == SystemDictionary::reflect_ConstructorAccessorImpl_klass() && + classname_matches_prefix(k, "jdk.internal.reflect.GeneratedSerializationConstructorAccessor")) { + return true; + } + return false; +} + +const char* ReflectionAccessorImplKlassHelper::get_target_class_name(const InstanceKlass* k) { + return get_string_from_cp_with_checks(k, cpi_slot_target_class_name); +} + +const char* ReflectionAccessorImplKlassHelper::get_target_method_name(const InstanceKlass* k) { + const int target_method_name_cpi = + is_generated_method_serialization_constructor_accessor(k) ? cpi_slot_target_method_name_sca : cpi_slot_target_method_name; + return get_string_from_cp_with_checks(k, target_method_name_cpi); +} + +const char* ReflectionAccessorImplKlassHelper::get_target_method_signature(const InstanceKlass* k) { + const int target_method_name_cpi = + is_generated_method_serialization_constructor_accessor(k) ? cpi_slot_target_method_sig_sca : cpi_slot_target_method_sig; + return get_string_from_cp_with_checks(k, target_method_name_cpi); +} + +// Returns true if this is either one of jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX +// and it is safe to call print_invocation_target(k) +bool ReflectionAccessorImplKlassHelper::is_generated_accessor(const Klass* k) { + if (k != NULL && k->is_instance_klass()) { + const InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->is_initialized()) { + return is_generated_method_accessor(ik) || + is_generated_constructor_accessor(ik) || + is_generated_method_serialization_constructor_accessor(ik); + } + } + return false; +} +void ReflectionAccessorImplKlassHelper::print_invocation_target(outputStream* out, Klass* k) { + assert(ReflectionAccessorImplKlassHelper::is_generated_accessor(k), "Invariant"); + InstanceKlass* ik = InstanceKlass::cast(k); + ResourceMark rm; + const char* target_class_name = ReflectionAccessorImplKlassHelper::get_target_class_name(ik); + const char* target_method_name = ReflectionAccessorImplKlassHelper::get_target_method_name(ik); + const char* target_method_signature = ReflectionAccessorImplKlassHelper::get_target_method_signature(ik); + out->print("%s::%s %s", + target_class_name != NULL ? target_class_name : "?", + target_method_name != NULL ? target_method_name : "?", + target_method_signature != NULL ? target_method_signature : "?"); +} diff --git a/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.hpp b/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.hpp new file mode 100644 index 00000000000..169497c7d45 --- /dev/null +++ b/src/hotspot/share/oops/reflectionAccessorImplKlassHelper.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 HOTSPOT_SHARE_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP +#define HOTSPOT_SHARE_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP + +#include "memory/allocation.hpp" + +class InstanceKlass; + +// Helper for classes derived from jdk/internal/reflect/{Method|Constructor}AccessorImpl: +// offers convenience functions to extract the names of target class/method/signature +// from the constant pool of these classes. +class ReflectionAccessorImplKlassHelper: public AllStatic { + + // Returns true if k is of type jdk/internal/reflect/GeneratedMethodAccessorXXX. + static bool is_generated_method_accessor(const InstanceKlass* k); + + // Returns true if k is of type jdk/internal/reflect/GeneratedConstructorAccessorXXX. + static bool is_generated_constructor_accessor(const InstanceKlass* k); + + // Returns true if k is of type jdk/internal/reflect/GeneratedSerializationConstructorAccessorXXX. + static bool is_generated_method_serialization_constructor_accessor(const InstanceKlass* k); + + // Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX, + // the name of the target class as resource-area allocated string. + static const char* get_target_class_name(const InstanceKlass* k); + + // Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX, + // the name of the target method as resource-area allocated string. + static const char* get_target_method_name(const InstanceKlass* k); + + // Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX, + // the signature of the target method as resource-area allocated string. + static const char* get_target_method_signature(const InstanceKlass* k); + +public: + + // Returns true if k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX + // and it is safe to call print_invocation_target(k) + static bool is_generated_accessor(const Klass* k); + + // Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX, + // print out target class, method, signature in one line. + static void print_invocation_target(outputStream* out, Klass* k); + +}; + + + + +#endif /* HOTSPOT_SHARE_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP */ + diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 5026ee315e9..00a81820568 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -887,7 +887,9 @@ bool SuperWord::ref_is_alignable(SWPointer& p) { if (init_nd->is_Con() && p.invar() == NULL) { int init = init_nd->bottom_type()->is_int()->get_con(); int init_offset = init * p.scale_in_bytes() + offset; - assert(init_offset >= 0, "positive offset from object start"); + if (init_offset < 0) { // negative offset from object start? + return false; // may happen in dead loop + } if (vw % span == 0) { // If vm is a multiple of span, we use formula (1). if (span > 0) { diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml index cd13b8de53a..4d54791618b 100644 --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -10353,6 +10353,14 @@ myInit() { See . + + + Can generate sampled allocation events. + If this capability is enabled then the heap sampling method + can be + called and events can be generated. + + @@ -11531,6 +11539,47 @@ myInit() { + + + Set Heap Sampling Rate + + Generate a event when objects are allocated. + Each thread keeps a counter of bytes allocated. The event will only be generated + when that counter exceeds an average of + since the last sample. +

+ Setting to 0 will cause an event to be + generated by each allocation supported by the system. + + new + + + + + + + + The sampling rate in bytes. The sampler uses a statistical approach to + generate an event, on average, once for every bytes of + memory allocated by a given thread. +

+ Passing 0 as a sampling rate generates a sample for every allocation. +

+ Note: The overhead of this feature is directly correlated with the sampling rate. + A high sampling rate, such as 1024 bytes, will incur a high overhead. + A lower rate, such as 1024KB, will have a much lower overhead. Sampling should only + be used with an understanding that it may impact performance. + + + + + + is less than zero. + + + + + @@ -13495,13 +13544,13 @@ myInit() { - JNI local reference to the object that was allocated + JNI local reference to the object that was allocated. - JNI local reference to the class of the object + JNI local reference to the class of the object. @@ -13513,8 +13562,75 @@ myInit() { + + + Sent when an allocated object is sampled. + By default, the sampling rate is a geometric variable with a 512KB mean. + Each thread tracks how many bytes it has allocated since it sent the last event. + When the number of bytes exceeds the sampling rate, it will send another event. + This implies that, on average, one object will be sampled every time a thread has + allocated 512KB bytes since the last sample. +

+ Note that this is a geometric variable: it will not sample every 512KB precisely. + The goal of this is to ensure high quality sampling even if allocation is + happening in a fixed pattern (i.e., the same set of objects are being allocated + every 512KB). +

+ If another sampling rate is required, the user can call + with a strictly positive integer value, representing + the new sampling rate. +

+ This event is sent once the sampled allocation has been performed. It provides the object, stack trace + of the allocation, the thread allocating, the size of allocation, and the object's class. +

+ A typical use case of this system is to determine where heap allocations originate. + In conjunction with weak references and the function + , a user can track which objects were allocated from which + stack trace, and which are still live during the execution of the program. + + new + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + Thread allocating the object. + + + + + + JNI local reference to the object that was allocated. + + + + + + JNI local reference to the class of the object + + + + + + Size of the object (in bytes). See . + + + + + + id="ObjectFree" const="JVMTI_EVENT_OBJECT_FREE" num="83"> An Object Free event is sent when the garbage collector frees an object. Events are only sent for tagged objects--see @@ -13534,7 +13650,7 @@ myInit() { The freed object's tag - + diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index cd55c690bd4..c2676904ce0 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -64,6 +64,7 @@ #include "runtime/reflectionUtils.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadHeapSampler.hpp" #include "runtime/threadSMR.hpp" #include "runtime/timerTrace.hpp" #include "runtime/vframe.inline.hpp" @@ -537,10 +538,17 @@ JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, j if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { record_class_file_load_hook_enabled(); } + + if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) { + if (enabled) { + ThreadHeapSampler::enable(); + } else { + ThreadHeapSampler::disable(); + } + } JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled); } else { // We have a specified event_thread. - JavaThread* java_thread = NULL; ThreadsListHandle tlh; jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL); @@ -3631,6 +3639,15 @@ JvmtiEnv::GetAvailableProcessors(jint* processor_count_ptr) { return JVMTI_ERROR_NONE; } /* end GetAvailableProcessors */ +jvmtiError +JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) { + if (sampling_rate < 0) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + ThreadHeapSampler::set_sampling_rate(sampling_rate); + return JVMTI_ERROR_NONE; +} /* end SetHeapSamplingRate */ + // // System Properties functions // diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index c92e3fb64b9..0c0a88e3041 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -84,6 +84,7 @@ static const jlong GARBAGE_COLLECTION_FINISH_BIT = (((jlong)1) << (JVMTI_EVENT_ static const jlong OBJECT_FREE_BIT = (((jlong)1) << (JVMTI_EVENT_OBJECT_FREE - TOTAL_MIN_EVENT_TYPE_VAL)); static const jlong RESOURCE_EXHAUSTED_BIT = (((jlong)1) << (JVMTI_EVENT_RESOURCE_EXHAUSTED - TOTAL_MIN_EVENT_TYPE_VAL)); static const jlong VM_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_VM_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong SAMPLED_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_SAMPLED_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL)); // bits for extension events static const jlong CLASS_UNLOAD_BIT = (((jlong)1) << (EXT_EVENT_CLASS_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL)); @@ -620,6 +621,7 @@ JvmtiEventControllerPrivate::recompute_enabled() { JvmtiExport::set_should_post_compiled_method_load((any_env_thread_enabled & COMPILED_METHOD_LOAD_BIT) != 0); JvmtiExport::set_should_post_compiled_method_unload((any_env_thread_enabled & COMPILED_METHOD_UNLOAD_BIT) != 0); JvmtiExport::set_should_post_vm_object_alloc((any_env_thread_enabled & VM_OBJECT_ALLOC_BIT) != 0); + JvmtiExport::set_should_post_sampled_object_alloc((any_env_thread_enabled & SAMPLED_OBJECT_ALLOC_BIT) != 0); // need this if we want thread events or we need them to init data JvmtiExport::set_should_post_thread_life((any_env_thread_enabled & NEED_THREAD_LIFE_EVENTS) != 0); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index db1ba197ffd..5dfe27e70bf 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1028,12 +1028,12 @@ static inline Klass* oop_to_klass(oop obj) { return k; } -class JvmtiVMObjectAllocEventMark : public JvmtiClassEventMark { +class JvmtiObjectAllocEventMark : public JvmtiClassEventMark { private: jobject _jobj; jlong _size; public: - JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) { + JvmtiObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) { _jobj = (jobject)to_jobject(obj); _size = obj->size() * wordSize; }; @@ -1198,6 +1198,7 @@ bool JvmtiExport::_should_post_garbage_collection_finish = fals bool JvmtiExport::_should_post_object_free = false; bool JvmtiExport::_should_post_resource_exhausted = false; bool JvmtiExport::_should_post_vm_object_alloc = false; +bool JvmtiExport::_should_post_sampled_object_alloc = false; bool JvmtiExport::_should_post_on_exceptions = false; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -2280,7 +2281,7 @@ void JvmtiExport::record_vm_internal_object_allocation(oop obj) { // Can not take safepoint here so can not use state_for to get // jvmti thread state. JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state(); - if (state != NULL ) { + if (state != NULL) { // state is non NULL when VMObjectAllocEventCollector is enabled. JvmtiVMObjectAllocEventCollector *collector; collector = state->get_vm_object_alloc_event_collector(); @@ -2295,6 +2296,27 @@ void JvmtiExport::record_vm_internal_object_allocation(oop obj) { } } +// Collect all the sampled allocated objects. +void JvmtiExport::record_sampled_internal_object_allocation(oop obj) { + Thread* thread = Thread::current_or_null(); + if (thread != NULL && thread->is_Java_thread()) { + // Can not take safepoint here. + NoSafepointVerifier no_sfpt; + // Can not take safepoint here so can not use state_for to get + // jvmti thread state. + JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state(); + if (state != NULL) { + // state is non NULL when SampledObjectAllocEventCollector is enabled. + JvmtiSampledObjectAllocEventCollector *collector; + collector = state->get_sampled_object_alloc_event_collector(); + + if (collector != NULL && collector->is_enabled()) { + collector->record_allocation(obj); + } + } + } +} + void JvmtiExport::post_garbage_collection_finish() { Thread *thread = Thread::current(); // this event is posted from VM-Thread. EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, @@ -2484,8 +2506,7 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt } } - -void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { +void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered", JvmtiTrace::safe_get_thread_name(thread))); if (object == NULL) { @@ -2500,7 +2521,7 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { JvmtiTrace::safe_get_thread_name(thread), object==NULL? "NULL" : object->klass()->external_name())); - JvmtiVMObjectAllocEventMark jem(thread, h()); + JvmtiObjectAllocEventMark jem(thread, h()); JvmtiJavaThreadEventTransition jet(thread); jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc; if (callback != NULL) { @@ -2511,6 +2532,34 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { } } +void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { + EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + ("[%s] Trg sampled object alloc triggered", + JvmtiTrace::safe_get_thread_name(thread))); + if (object == NULL) { + return; + } + HandleMark hm(thread); + Handle h(thread, object); + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) { + EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + ("[%s] Evt sampled object alloc sent %s", + JvmtiTrace::safe_get_thread_name(thread), + object == NULL ? "NULL" : object->klass()->external_name())); + + JvmtiObjectAllocEventMark jem(thread, h()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_jobject(), jem.jni_class(), jem.size()); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////////////////////// void JvmtiExport::cleanup_thread(JavaThread* thread) { @@ -2536,7 +2585,7 @@ void JvmtiExport::clear_detected_exception(JavaThread* thread) { void JvmtiExport::oops_do(OopClosure* f) { JvmtiCurrentBreakpoints::oops_do(f); - JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(f); + JvmtiObjectAllocEventCollector::oops_do_for_all_threads(f); } void JvmtiExport::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { @@ -2669,12 +2718,28 @@ void JvmtiEventCollector::setup_jvmti_thread_state() { } else if (is_dynamic_code_event()) { _prev = state->get_dynamic_code_event_collector(); state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this); + } else if (is_sampled_object_alloc_event()) { + JvmtiSampledObjectAllocEventCollector *prev = state->get_sampled_object_alloc_event_collector(); + + if (prev) { + // JvmtiSampledObjectAllocEventCollector wants only one active collector + // enabled. This allows to have a collector detect a user code requiring + // a sample in the callback. + return; + } + state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*) this); } + + _unset_jvmti_thread_state = true; } // Unset current event collection in this thread and reset it with previous // collector. void JvmtiEventCollector::unset_jvmti_thread_state() { + if (!_unset_jvmti_thread_state) { + return; + } + JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state(); if (state != NULL) { // restore the previous event collector (if any) @@ -2685,14 +2750,19 @@ void JvmtiEventCollector::unset_jvmti_thread_state() { // this thread's jvmti state was created during the scope of // the event collector. } - } else { - if (is_dynamic_code_event()) { - if (state->get_dynamic_code_event_collector() == this) { - state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev); - } else { - // this thread's jvmti state was created during the scope of - // the event collector. - } + } else if (is_dynamic_code_event()) { + if (state->get_dynamic_code_event_collector() == this) { + state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. + } + } else if (is_sampled_object_alloc_event()) { + if (state->get_sampled_object_alloc_event_collector() == this) { + state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. } } } @@ -2730,31 +2800,25 @@ void JvmtiDynamicCodeEventCollector::register_stub(const char* name, address sta } // Setup current thread to record vm allocated objects. -JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() : _allocated(NULL) { - if (JvmtiExport::should_post_vm_object_alloc()) { - _enable = true; - setup_jvmti_thread_state(); - } else { - _enable = false; - } +JvmtiObjectAllocEventCollector::JvmtiObjectAllocEventCollector() : + _allocated(NULL), _enable(false), _post_callback(NULL) { } // Post vm_object_alloc event for vm allocated objects visible to java // world. -JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { - if (_allocated != NULL) { +void JvmtiObjectAllocEventCollector::generate_call_for_allocated() { + if (_allocated) { set_enabled(false); for (int i = 0; i < _allocated->length(); i++) { oop obj = _allocated->at(i); - JvmtiExport::post_vm_object_alloc(JavaThread::current(), obj); + _post_callback(JavaThread::current(), obj); } - delete _allocated; + delete _allocated, _allocated = NULL; } - unset_jvmti_thread_state(); } -void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) { - assert(is_enabled(), "VM object alloc event collector is not enabled"); +void JvmtiObjectAllocEventCollector::record_allocation(oop obj) { + assert(is_enabled(), "Object alloc event collector is not enabled"); if (_allocated == NULL) { _allocated = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(1, true); } @@ -2762,9 +2826,9 @@ void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) { } // GC support. -void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) { - if (_allocated != NULL) { - for(int i=_allocated->length() - 1; i >= 0; i--) { +void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) { + if (_allocated) { + for(int i = _allocated->length() - 1; i >= 0; i--) { if (_allocated->at(i) != NULL) { f->do_oop(_allocated->adr_at(i)); } @@ -2772,7 +2836,7 @@ void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) { } } -void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { +void JvmtiObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { // no-op if jvmti not enabled if (!JvmtiEnv::environments_might_exist()) { return; @@ -2781,11 +2845,17 @@ void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) { JvmtiThreadState *state = jthr->jvmti_thread_state(); if (state != NULL) { - JvmtiVMObjectAllocEventCollector *collector; + JvmtiObjectAllocEventCollector *collector; collector = state->get_vm_object_alloc_event_collector(); while (collector != NULL) { collector->oops_do(f); - collector = (JvmtiVMObjectAllocEventCollector *)collector->get_prev(); + collector = (JvmtiObjectAllocEventCollector*) collector->get_prev(); + } + + collector = state->get_sampled_object_alloc_event_collector(); + while (collector != NULL) { + collector->oops_do(f); + collector = (JvmtiObjectAllocEventCollector*) collector->get_prev(); } } } @@ -2820,6 +2890,63 @@ NoJvmtiVMObjectAllocMark::~NoJvmtiVMObjectAllocMark() { } }; +// Setup current thread to record vm allocated objects. +JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() { + if (JvmtiExport::should_post_vm_object_alloc()) { + _enable = true; + setup_jvmti_thread_state(); + _post_callback = JvmtiExport::post_vm_object_alloc; + } +} + +JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { + if (_enable) { + generate_call_for_allocated(); + } + unset_jvmti_thread_state(); +} + +bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() { + Thread* thread = Thread::current(); + // Really only sample allocations if this is a JavaThread and not the compiler + // thread. + if (!thread->is_Java_thread() || thread->is_Compiler_thread()) { + return false; + } + + if (Compile_lock->owner() == thread || + MultiArray_lock->owner() == thread) { + return false; + } + return true; +} + +// Setup current thread to record sampled allocated objects. +JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() { + if (JvmtiExport::should_post_sampled_object_alloc()) { + if (!object_alloc_is_safe_to_sample()) { + return; + } + + _enable = true; + setup_jvmti_thread_state(); + _post_callback = JvmtiExport::post_sampled_object_alloc; + } +} + +JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() { + if (!_enable) { + return; + } + + generate_call_for_allocated(); + unset_jvmti_thread_state(); + + // Unset the sampling collector as present in assertion mode only. + assert(Thread::current()->is_Java_thread(), + "Should always be in a Java thread"); +} + JvmtiGCMarker::JvmtiGCMarker() { // if there aren't any JVMTI environments then nothing to do if (!JvmtiEnv::environments_might_exist()) { diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index 7e8fb2c8999..0e27799472b 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -123,6 +123,7 @@ class JvmtiExport : public AllStatic { // breakpoint info JVMTI_SUPPORT_FLAG(should_clean_up_heap_objects) JVMTI_SUPPORT_FLAG(should_post_vm_object_alloc) + JVMTI_SUPPORT_FLAG(should_post_sampled_object_alloc) // If flag cannot be implemented, give an error if on=true static void report_unsupported(bool on); @@ -363,6 +364,18 @@ class JvmtiExport : public AllStatic { record_vm_internal_object_allocation(object); } } + + static void record_sampled_internal_object_allocation(oop object) NOT_JVMTI_RETURN; + // Post objects collected by sampled_object_alloc_event_collector. + static void post_sampled_object_alloc(JavaThread *thread, oop object) NOT_JVMTI_RETURN; + + // Collects vm internal objects for later event posting. + inline static void sampled_object_alloc_event_collector(oop object) { + if (should_post_sampled_object_alloc()) { + record_sampled_internal_object_allocation(object); + } + } + inline static void post_array_size_exhausted() { if (should_post_resource_exhausted()) { post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, @@ -422,12 +435,16 @@ class JvmtiCodeBlobDesc : public CHeapObj { class JvmtiEventCollector : public StackObj { private: JvmtiEventCollector* _prev; // Save previous one to support nested event collector. + bool _unset_jvmti_thread_state; public: - void setup_jvmti_thread_state(); // Set this collector in current thread. + JvmtiEventCollector() : _prev(NULL), _unset_jvmti_thread_state(false) {} + + void setup_jvmti_thread_state(); // Set this collector in current thread, returns if success. void unset_jvmti_thread_state(); // Reset previous collector in current thread. virtual bool is_dynamic_code_event() { return false; } virtual bool is_vm_object_alloc_event(){ return false; } + virtual bool is_sampled_object_alloc_event(){ return false; } JvmtiEventCollector *get_prev() { return _prev; } }; @@ -462,42 +479,67 @@ class JvmtiDynamicCodeEventCollector : public JvmtiEventCollector { }; -// Used to record vm internally allocated object oops and post -// vm object alloc event for objects visible to java world. -// Constructor enables JvmtiThreadState flag and all vm allocated -// objects are recorded in a growable array. When destructor is -// called the vm object alloc event is posted for each objects -// visible to java world. -// See jvm.cpp file for its usage. +// Used as a base class for object allocation collection and then posting +// the allocations to any event notification callbacks. // -class JvmtiVMObjectAllocEventCollector : public JvmtiEventCollector { - private: - GrowableArray* _allocated; // field to record vm internally allocated object oop. - bool _enable; // This flag is enabled in constructor and disabled - // in destructor before posting event. To avoid +class JvmtiObjectAllocEventCollector : public JvmtiEventCollector { + protected: + GrowableArray* _allocated; // field to record collected allocated object oop. + bool _enable; // This flag is enabled in constructor if set up in the thread state + // and disabled in destructor before posting event. To avoid // collection of objects allocated while running java code inside - // agent post_vm_object_alloc() event handler. + // agent post_X_object_alloc() event handler. + void (*_post_callback)(JavaThread*, oop); // what callback to use when destroying the collector. //GC support void oops_do(OopClosure* f); friend class JvmtiExport; - // Record vm allocated object oop. + + // Record allocated object oop. inline void record_allocation(oop obj); //GC support static void oops_do_for_all_threads(OopClosure* f); public: - JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; - ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; - bool is_vm_object_alloc_event() { return true; } + JvmtiObjectAllocEventCollector() NOT_JVMTI_RETURN; + + void generate_call_for_allocated(); bool is_enabled() { return _enable; } void set_enabled(bool on) { _enable = on; } }; +// Used to record vm internally allocated object oops and post +// vm object alloc event for objects visible to java world. +// Constructor enables JvmtiThreadState flag and all vm allocated +// objects are recorded in a growable array. When destructor is +// called the vm object alloc event is posted for each object +// visible to java world. +// See jvm.cpp file for its usage. +// +class JvmtiVMObjectAllocEventCollector : public JvmtiObjectAllocEventCollector { + public: + JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; + ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; + virtual bool is_vm_object_alloc_event() { return true; } +}; +// Used to record sampled allocated object oops and post +// sampled object alloc event. +// Constructor enables JvmtiThreadState flag and all sampled allocated +// objects are recorded in a growable array. When destructor is +// called the sampled object alloc event is posted for each sampled object. +// See jvm.cpp file for its usage. +// +class JvmtiSampledObjectAllocEventCollector : public JvmtiObjectAllocEventCollector { + public: + JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN; + ~JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN; + bool is_sampled_object_alloc_event() { return true; } + static bool object_alloc_is_safe_to_sample(); +}; // Marker class to disable the posting of VMObjectAlloc events // within its scope. diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp index 3ccc5c1614f..f1ab1c2d6dc 100644 --- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp @@ -130,6 +130,7 @@ jvmtiCapabilities JvmtiManageCapabilities::init_always_solo_capabilities() { memset(&jc, 0, sizeof(jc)); jc.can_suspend = 1; + jc.can_generate_sampled_object_alloc_events = 1; return jc; } @@ -410,6 +411,8 @@ void JvmtiManageCapabilities:: print(const jvmtiCapabilities* cap) { log_trace(jvmti)("can_generate_frame_pop_events"); if (cap->can_generate_breakpoint_events) log_trace(jvmti)("can_generate_breakpoint_events"); + if (cap->can_generate_sampled_object_alloc_events) + log_trace(jvmti)("can_generate_sampled_object_alloc_events"); if (cap->can_suspend) log_trace(jvmti)("can_suspend"); if (cap->can_redefine_any_class ) diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index fca81e0ad8b..5acd73af396 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -60,6 +60,7 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread) _head_env_thread_state = NULL; _dynamic_code_event_collector = NULL; _vm_object_alloc_event_collector = NULL; + _sampled_object_alloc_event_collector = NULL; _the_class_for_redefinition_verification = NULL; _scratch_class_for_redefinition_verification = NULL; _cur_stack_depth = UNKNOWN_STACK_DEPTH; diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 16a0a3455c7..a187b9caae2 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -113,6 +113,8 @@ class JvmtiThreadState : public CHeapObj { JvmtiDynamicCodeEventCollector* _dynamic_code_event_collector; // holds the current vm object alloc event collector, NULL if no event collector in use JvmtiVMObjectAllocEventCollector* _vm_object_alloc_event_collector; + // holds the current sampled object alloc event collector, NULL if no event collector in use + JvmtiSampledObjectAllocEventCollector* _sampled_object_alloc_event_collector; // Should only be created by factory methods JvmtiThreadState(JavaThread *thread); @@ -314,12 +316,18 @@ class JvmtiThreadState : public CHeapObj { JvmtiVMObjectAllocEventCollector* get_vm_object_alloc_event_collector() { return _vm_object_alloc_event_collector; } + JvmtiSampledObjectAllocEventCollector* get_sampled_object_alloc_event_collector() { + return _sampled_object_alloc_event_collector; + } void set_dynamic_code_event_collector(JvmtiDynamicCodeEventCollector* collector) { _dynamic_code_event_collector = collector; } void set_vm_object_alloc_event_collector(JvmtiVMObjectAllocEventCollector* collector) { _vm_object_alloc_event_collector = collector; } + void set_sampled_object_alloc_event_collector(JvmtiSampledObjectAllocEventCollector* collector) { + _sampled_object_alloc_event_collector = collector; + } // diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 3af2ad7091e..9e86fb5cff0 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -116,6 +116,7 @@ SystemProperty *Arguments::_java_library_path = NULL; SystemProperty *Arguments::_java_home = NULL; SystemProperty *Arguments::_java_class_path = NULL; SystemProperty *Arguments::_jdk_boot_class_path_append = NULL; +SystemProperty *Arguments::_vm_info = NULL; GrowableArray *Arguments::_patch_mod_prefix = NULL; PathString *Arguments::_system_boot_class_path = NULL; @@ -395,9 +396,11 @@ void Arguments::init_system_properties() { "Java Virtual Machine Specification", false)); PropertyList_add(&_system_properties, new SystemProperty("java.vm.version", VM_Version::vm_release(), false)); PropertyList_add(&_system_properties, new SystemProperty("java.vm.name", VM_Version::vm_name(), false)); - PropertyList_add(&_system_properties, new SystemProperty("java.vm.info", VM_Version::vm_info_string(), true)); PropertyList_add(&_system_properties, new SystemProperty("jdk.debug", VM_Version::jdk_debug_level(), false)); + // Initialize the vm.info now, but it will need updating after argument parsing. + _vm_info = new SystemProperty("java.vm.info", VM_Version::vm_info_string(), true); + // Following are JVMTI agent writable properties. // Properties values are set to NULL and they are // os specific they are initialized in os::init_system_properties_values(). @@ -417,6 +420,7 @@ void Arguments::init_system_properties() { PropertyList_add(&_system_properties, _java_home); PropertyList_add(&_system_properties, _java_class_path); PropertyList_add(&_system_properties, _jdk_boot_class_path_append); + PropertyList_add(&_system_properties, _vm_info); // Set OS specific system properties values os::init_system_properties_values(); @@ -1588,101 +1592,6 @@ static void no_shared_spaces(const char* message) { } } -// Returns threshold scaled with the value of scale. -// If scale < 0.0, threshold is returned without scaling. -intx Arguments::scaled_compile_threshold(intx threshold, double scale) { - if (scale == 1.0 || scale < 0.0) { - return threshold; - } else { - return (intx)(threshold * scale); - } -} - -// Returns freq_log scaled with the value of scale. -// Returned values are in the range of [0, InvocationCounter::number_of_count_bits + 1]. -// If scale < 0.0, freq_log is returned without scaling. -intx Arguments::scaled_freq_log(intx freq_log, double scale) { - // Check if scaling is necessary or if negative value was specified. - if (scale == 1.0 || scale < 0.0) { - return freq_log; - } - // Check values to avoid calculating log2 of 0. - if (scale == 0.0 || freq_log == 0) { - return 0; - } - // Determine the maximum notification frequency value currently supported. - // The largest mask value that the interpreter/C1 can handle is - // of length InvocationCounter::number_of_count_bits. Mask values are always - // one bit shorter then the value of the notification frequency. Set - // max_freq_bits accordingly. - intx max_freq_bits = InvocationCounter::number_of_count_bits + 1; - intx scaled_freq = scaled_compile_threshold((intx)1 << freq_log, scale); - if (scaled_freq == 0) { - // Return 0 right away to avoid calculating log2 of 0. - return 0; - } else if (scaled_freq > nth_bit(max_freq_bits)) { - return max_freq_bits; - } else { - return log2_intptr(scaled_freq); - } -} - -void Arguments::set_tiered_flags() { - // With tiered, set default policy to SimpleThresholdPolicy, which is 2. - if (FLAG_IS_DEFAULT(CompilationPolicyChoice)) { - FLAG_SET_DEFAULT(CompilationPolicyChoice, 2); - } - if (CompilationPolicyChoice < 2) { - vm_exit_during_initialization( - "Incompatible compilation policy selected", NULL); - } - // Increase the code cache size - tiered compiles a lot more. - if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_ERGO(uintx, ReservedCodeCacheSize, - MIN2(CODE_CACHE_DEFAULT_LIMIT, ReservedCodeCacheSize * 5)); - } - // Enable SegmentedCodeCache if TieredCompilation is enabled and ReservedCodeCacheSize >= 240M - if (FLAG_IS_DEFAULT(SegmentedCodeCache) && ReservedCodeCacheSize >= 240*M) { - FLAG_SET_ERGO(bool, SegmentedCodeCache, true); - } - if (!UseInterpreter) { // -Xcomp - Tier3InvokeNotifyFreqLog = 0; - Tier4InvocationThreshold = 0; - } - - if (CompileThresholdScaling < 0) { - vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", NULL); - } - - // Scale tiered compilation thresholds. - // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves compilation thresholds unchanged. - if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { - FLAG_SET_ERGO(intx, Tier0InvokeNotifyFreqLog, scaled_freq_log(Tier0InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier0BackedgeNotifyFreqLog, scaled_freq_log(Tier0BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier3InvocationThreshold, scaled_compile_threshold(Tier3InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3MinInvocationThreshold, scaled_compile_threshold(Tier3MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3CompileThreshold, scaled_compile_threshold(Tier3CompileThreshold)); - FLAG_SET_ERGO(intx, Tier3BackEdgeThreshold, scaled_compile_threshold(Tier3BackEdgeThreshold)); - - // Tier2{Invocation,MinInvocation,Compile,Backedge}Threshold should be scaled here - // once these thresholds become supported. - - FLAG_SET_ERGO(intx, Tier2InvokeNotifyFreqLog, scaled_freq_log(Tier2InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier2BackedgeNotifyFreqLog, scaled_freq_log(Tier2BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier3InvokeNotifyFreqLog, scaled_freq_log(Tier3InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier3BackedgeNotifyFreqLog, scaled_freq_log(Tier3BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier23InlineeNotifyFreqLog, scaled_freq_log(Tier23InlineeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier4InvocationThreshold, scaled_compile_threshold(Tier4InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold)); - FLAG_SET_ERGO(intx, Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold)); - } -} - void set_object_alignment() { // Object alignment. assert(is_power_of_2(ObjectAlignmentInBytes), "ObjectAlignmentInBytes must be power of 2"); @@ -1783,74 +1692,9 @@ void Arguments::set_conservative_max_heap_alignment() { CollectorPolicy::compute_heap_alignment()); } -#ifdef TIERED -bool Arguments::compilation_mode_selected() { - return !FLAG_IS_DEFAULT(TieredCompilation) || !FLAG_IS_DEFAULT(TieredStopAtLevel) || - !FLAG_IS_DEFAULT(UseAOT) JVMCI_ONLY(|| !FLAG_IS_DEFAULT(EnableJVMCI) || !FLAG_IS_DEFAULT(UseJVMCICompiler)); - -} - -void Arguments::select_compilation_mode_ergonomically() { -#if defined(_WINDOWS) && !defined(_LP64) - if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { - FLAG_SET_ERGO(bool, NeverActAsServerClassMachine, true); - } -#endif - if (NeverActAsServerClassMachine) { - set_client_compilation_mode(); - } -} -#endif //TIERED - -#if INCLUDE_JVMCI -void Arguments::set_jvmci_specific_flags() { - if (UseJVMCICompiler) { - if (FLAG_IS_DEFAULT(TypeProfileWidth)) { - FLAG_SET_DEFAULT(TypeProfileWidth, 8); - } - if (FLAG_IS_DEFAULT(OnStackReplacePercentage)) { - FLAG_SET_DEFAULT(OnStackReplacePercentage, 933); - } - if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_DEFAULT(ReservedCodeCacheSize, 64*M); - } - if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { - FLAG_SET_DEFAULT(InitialCodeCacheSize, 16*M); - } - if (FLAG_IS_DEFAULT(MetaspaceSize)) { - FLAG_SET_DEFAULT(MetaspaceSize, 12*M); - } - if (FLAG_IS_DEFAULT(NewSizeThreadIncrease)) { - FLAG_SET_DEFAULT(NewSizeThreadIncrease, 4*K); - } - if (TieredStopAtLevel != CompLevel_full_optimization) { - // Currently JVMCI compiler can only work at the full optimization level - warning("forcing TieredStopAtLevel to full optimization because JVMCI is enabled"); - TieredStopAtLevel = CompLevel_full_optimization; - } - if (FLAG_IS_DEFAULT(TypeProfileLevel)) { - FLAG_SET_DEFAULT(TypeProfileLevel, 0); - } - } -} -#endif - jint Arguments::set_ergonomics_flags() { -#ifdef TIERED - if (!compilation_mode_selected()) { - select_compilation_mode_ergonomically(); - } -#endif - GCConfig::initialize(); -#if defined(IA32) - // Only server compiler can optimize safepoints well enough. - if (!is_server_compilation_mode_vm()) { - FLAG_SET_ERGO_IF_DEFAULT(bool, ThreadLocalHandshakes, false); - } -#endif - set_conservative_max_heap_alignment(); #ifndef ZERO @@ -2181,12 +2025,11 @@ bool Arguments::sun_java_launcher_is_altjvm() { //=========================================================================================================== // Parsing of main arguments -#if INCLUDE_JVMCI -// Check consistency of jvmci vm argument settings. -bool Arguments::check_jvmci_args_consistency() { - return JVMCIGlobals::check_jvmci_flags_are_consistent(); -} -#endif //INCLUDE_JVMCI +unsigned int addreads_count = 0; +unsigned int addexports_count = 0; +unsigned int addopens_count = 0; +unsigned int addmods_count = 0; +unsigned int patch_mod_count = 0; // Check the consistency of vm_init_args bool Arguments::check_vm_args_consistency() { @@ -2215,52 +2058,17 @@ bool Arguments::check_vm_args_consistency() { #endif } + status = CompilerConfig::check_args_consistency(status); #if INCLUDE_JVMCI - status = status && check_jvmci_args_consistency(); - - if (EnableJVMCI) { + if (status && EnableJVMCI) { PropertyList_unique_add(&_system_properties, "jdk.internal.vm.ci.enabled", "true", AddProperty, UnwriteableProperty, InternalProperty); - - if (!ScavengeRootsInCode) { - warning("forcing ScavengeRootsInCode non-zero because JVMCI is enabled"); - ScavengeRootsInCode = 1; + if (!create_numbered_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) { + return false; } } #endif - // 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 (ReservedCodeCacheSize < InitialCodeCacheSize) { - jio_fprintf(defaultStream::error_stream(), - "Invalid ReservedCodeCacheSize: %dK. Must be at least InitialCodeCacheSize=%dK.\n", - ReservedCodeCacheSize/K, InitialCodeCacheSize/K); - status = false; - } else if (ReservedCodeCacheSize < min_code_cache_size) { - jio_fprintf(defaultStream::error_stream(), - "Invalid ReservedCodeCacheSize=%dK. Must be at least %uK.\n", ReservedCodeCacheSize/K, - min_code_cache_size/K); - status = false; - } else if (ReservedCodeCacheSize > CODE_CACHE_SIZE_LIMIT) { - // Code cache size larger than CODE_CACHE_SIZE_LIMIT is not supported. - jio_fprintf(defaultStream::error_stream(), - "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M, - CODE_CACHE_SIZE_LIMIT/M); - status = false; - } else if (NonNMethodCodeHeapSize < min_code_cache_size) { - jio_fprintf(defaultStream::error_stream(), - "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K, - min_code_cache_size/K); - status = false; - } - -#ifdef _LP64 - if (!FLAG_IS_DEFAULT(CICompilerCount) && !FLAG_IS_DEFAULT(CICompilerCountPerCPU) && CICompilerCountPerCPU) { - warning("The VM option CICompilerCountPerCPU overrides CICompilerCount."); - } -#endif - #ifndef SUPPORT_RESERVED_STACK_AREA if (StackReservedPages != 0) { FLAG_SET_CMDLINE(intx, StackReservedPages, 0); @@ -2268,37 +2076,6 @@ bool Arguments::check_vm_args_consistency() { } #endif - if (BackgroundCompilation && (CompileTheWorld || ReplayCompiles)) { - if (!FLAG_IS_DEFAULT(BackgroundCompilation)) { - warning("BackgroundCompilation disabled due to CompileTheWorld or ReplayCompiles options."); - } - FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); - } - if (UseCompiler && is_interpreter_only()) { - if (!FLAG_IS_DEFAULT(UseCompiler)) { - warning("UseCompiler disabled due to -Xint."); - } - FLAG_SET_CMDLINE(bool, UseCompiler, false); - } -#ifdef COMPILER2 - if (PostLoopMultiversioning && !RangeCheckElimination) { - if (!FLAG_IS_DEFAULT(PostLoopMultiversioning)) { - warning("PostLoopMultiversioning disabled because RangeCheckElimination is disabled."); - } - 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; - } -#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"); @@ -2360,12 +2137,6 @@ bool Arguments::parse_uintx(const char* value, return false; } -unsigned int addreads_count = 0; -unsigned int addexports_count = 0; -unsigned int addopens_count = 0; -unsigned int addmods_count = 0; -unsigned int patch_mod_count = 0; - bool Arguments::create_property(const char* prop_name, const char* prop_value, PropertyInternal internal) { size_t prop_len = strlen(prop_name) + strlen(prop_value) + 2; char* property = AllocateHeap(prop_len, mtArguments); @@ -3354,13 +3125,6 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { FLAG_SET_DEFAULT(UseLargePages, false); } -#elif defined(COMPILER2) - if (!FLAG_IS_DEFAULT(OptoLoopAlignment) && FLAG_IS_DEFAULT(MaxLoopPad)) { - FLAG_SET_DEFAULT(MaxLoopPad, OptoLoopAlignment-1); - } -#endif - -#if !COMPILER2_OR_JVMCI UNSUPPORTED_OPTION(ProfileInterpreter); NOT_PRODUCT(UNSUPPORTED_OPTION(TraceProfileInterpreter)); #endif @@ -3374,19 +3138,6 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { return JNI_ERR; } -#if INCLUDE_JVMCI - if (EnableJVMCI && - !create_numbered_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) { - return JNI_ENOMEM; - } -#endif - -#if INCLUDE_JVMCI - if (UseJVMCICompiler) { - Compilation_mode = CompMode_server; - } -#endif - #if INCLUDE_CDS if (DumpSharedSpaces) { // Disable biased locking now as it interferes with the clean up of @@ -4145,41 +3896,8 @@ jint Arguments::apply_ergo() { jint result = set_ergonomics_flags(); if (result != JNI_OK) return result; -#if INCLUDE_JVMCI - set_jvmci_specific_flags(); -#endif - set_shared_spaces_flags(); - if (TieredCompilation) { - set_tiered_flags(); - } else { - int max_compilation_policy_choice = 1; -#ifdef COMPILER2 - if (is_server_compilation_mode_vm()) { - max_compilation_policy_choice = 2; - } -#endif - // Check if the policy is valid. - if (CompilationPolicyChoice >= max_compilation_policy_choice) { - vm_exit_during_initialization( - "Incompatible compilation policy selected", NULL); - } - // Scale CompileThreshold - // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves CompileThreshold unchanged. - if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { - FLAG_SET_ERGO(intx, CompileThreshold, scaled_compile_threshold(CompileThreshold)); - } - } - -#ifdef COMPILER2 -#ifndef PRODUCT - if (PrintIdealGraphLevel > 0) { - FLAG_SET_ERGO(bool, PrintIdealGraph, true); - } -#endif -#endif - // Set heap size based on available physical memory set_heap_size(); @@ -4188,6 +3906,10 @@ jint Arguments::apply_ergo() { // Initialize Metaspace flags and alignments Metaspace::ergo_initialize(); + // Set compiler flags after GC is selected and GC specific + // flags (LoopStripMiningIter) are set. + CompilerConfig::ergo_initialize(); + // Set bytecode rewriting flags set_bytecode_flags(); @@ -4225,28 +3947,6 @@ jint Arguments::apply_ergo() { LP64_ONLY(FLAG_SET_DEFAULT(UseCompressedClassPointers, false)); #endif // CC_INTERP -#ifdef COMPILER2 - if (!EliminateLocks) { - EliminateNestedLocks = false; - } - if (!Inline) { - IncrementalInline = false; - } -#ifndef PRODUCT - if (!IncrementalInline) { - AlwaysIncrementalInline = false; - } -#endif - if (!UseTypeSpeculation && FLAG_IS_DEFAULT(TypeProfileLevel)) { - // nothing to use the profiling, turn if off - FLAG_SET_DEFAULT(TypeProfileLevel, 0); - } - if (FLAG_IS_DEFAULT(LoopStripMiningIterShortLoop)) { - // blind guess - LoopStripMiningIterShortLoop = LoopStripMiningIter / 10; - } -#endif - if (PrintAssembly && FLAG_IS_DEFAULT(DebugNonSafepoints)) { warning("PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output"); DebugNonSafepoints = true; @@ -4256,11 +3956,6 @@ jint Arguments::apply_ergo() { warning("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used"); } - if (UseOnStackReplacement && !UseLoopCounter) { - warning("On-stack-replacement requires loop counters; enabling loop counters"); - FLAG_SET_DEFAULT(UseLoopCounter, true); - } - #ifndef PRODUCT if (!LogVMOutput && FLAG_IS_DEFAULT(LogVMOutput)) { if (use_vm_log()) { @@ -4286,6 +3981,13 @@ jint Arguments::apply_ergo() { } #endif +#if defined(IA32) + // Only server compiler can optimize safepoints well enough. + if (!is_server_compilation_mode_vm()) { + FLAG_SET_ERGO_IF_DEFAULT(bool, ThreadLocalHandshakes, false); + } +#endif + // ThreadLocalHandshakesConstraintFunc handles the constraints. if (FLAG_IS_DEFAULT(ThreadLocalHandshakes) || !SafepointMechanism::supports_thread_local_poll()) { log_debug(ergo)("ThreadLocalHandshakes %s", ThreadLocalHandshakes ? "enabled." : "disabled."); @@ -4451,18 +4153,6 @@ void Arguments::PropertyList_unique_add(SystemProperty** plist, const char* k, c PropertyList_add(plist, k, v, writeable == WriteableProperty, internal == InternalProperty); } -// Update existing property with new value. -void Arguments::PropertyList_update_value(SystemProperty* plist, const char* k, const char* v) { - SystemProperty* prop; - for (prop = plist; prop != NULL; prop = prop->next()) { - if (strcmp(k, prop->key()) == 0) { - prop->set_value(v); - return; - } - } - assert(false, "invalid property"); -} - // Copies src into buf, replacing "%%" with "%" and "%p" with pid // Returns true if all of the source pointed by src has been copied over to // the destination buffer pointed by buf. Otherwise, returns false. diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 42b9933130e..892398421a7 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -291,6 +291,7 @@ class Arguments : AllStatic { static SystemProperty *_java_home; static SystemProperty *_java_class_path; static SystemProperty *_jdk_boot_class_path_append; + static SystemProperty *_vm_info; // --patch-module=module=()* // Each element contains the associated module name, path @@ -371,13 +372,6 @@ class Arguments : AllStatic { static intx _Tier3InvokeNotifyFreqLog; static intx _Tier4InvocationThreshold; - // Compilation mode. - static bool compilation_mode_selected(); - static void select_compilation_mode_ergonomically(); - - // Tiered - static void set_tiered_flags(); - // GC ergonomics static void set_conservative_max_heap_alignment(); static void set_use_compressed_oops(); @@ -468,18 +462,6 @@ class Arguments : AllStatic { static void add_string(char*** bldarray, int* count, const char* arg); static const char* build_resource_string(char** args, int count); - static bool methodExists( - char* className, char* methodName, - int classesNum, char** classes, bool* allMethods, - int methodsNum, char** methods, bool* allClasses - ); - - static void parseOnlyLine( - const char* line, - short* classesNum, short* classesMax, char*** classes, bool** allMethods, - short* methodsNum, short* methodsMax, char*** methods, bool** allClasses - ); - // Returns true if the flag is obsolete (and not yet expired). // In this case the 'version' buffer is filled in with // the version number when the flag became obsolete. @@ -504,38 +486,10 @@ class Arguments : AllStatic { static const char* handle_aliases_and_deprecation(const char* arg, bool warn); static bool lookup_logging_aliases(const char* arg, char* buffer); static AliasedLoggingFlag catch_logging_aliases(const char* name, bool on); - static short CompileOnlyClassesNum; - static short CompileOnlyClassesMax; - static char** CompileOnlyClasses; - static bool* CompileOnlyAllMethods; - - static short CompileOnlyMethodsNum; - static short CompileOnlyMethodsMax; - static char** CompileOnlyMethods; - static bool* CompileOnlyAllClasses; - - static short InterpretOnlyClassesNum; - static short InterpretOnlyClassesMax; - static char** InterpretOnlyClasses; - static bool* InterpretOnlyAllMethods; - - static bool CheckCompileOnly; static char* SharedArchivePath; public: - // Scale compile thresholds - // Returns threshold scaled with CompileThresholdScaling - static intx scaled_compile_threshold(intx threshold, double scale); - static intx scaled_compile_threshold(intx threshold) { - return scaled_compile_threshold(threshold, CompileThresholdScaling); - } - // Returns freq_log scaled with CompileThresholdScaling - static intx scaled_freq_log(intx freq_log, double scale); - static intx scaled_freq_log(intx freq_log) { - return scaled_freq_log(freq_log, CompileThresholdScaling); - } - // Parses the arguments, first phase static jint parse(const JavaVMInitArgs* args); // Apply ergonomics @@ -543,11 +497,6 @@ class Arguments : AllStatic { // Adjusts the arguments after the OS have adjusted the arguments static jint adjust_after_os(); -#if INCLUDE_JVMCI - // Check consistency of jvmci vm argument settings. - static bool check_jvmci_args_consistency(); - static void set_jvmci_specific_flags(); -#endif // Check for consistency in the selection of the garbage collector. static bool check_gc_consistency(); // Check user-selected gc // Check consistency or otherwise of VM argument settings @@ -621,19 +570,8 @@ class Arguments : AllStatic { static exit_hook_t exit_hook() { return _exit_hook; } static vfprintf_hook_t vfprintf_hook() { return _vfprintf_hook; } - static bool GetCheckCompileOnly () { return CheckCompileOnly; } - static const char* GetSharedArchivePath() { return SharedArchivePath; } - static bool CompileMethod(char* className, char* methodName) { - return - methodExists( - className, methodName, - CompileOnlyClassesNum, CompileOnlyClasses, CompileOnlyAllMethods, - CompileOnlyMethodsNum, CompileOnlyMethods, CompileOnlyAllClasses - ); - } - // Java launcher properties static void process_sun_java_launcher_properties(JavaVMInitArgs* args); @@ -643,6 +581,11 @@ class Arguments : AllStatic { // Update/Initialize System properties after JDK version number is known static void init_version_specific_system_properties(); + // Update VM info property - called after argument parsing + static void update_vm_info_property(const char* vm_info) { + _vm_info->set_value(vm_info); + } + // Property List manipulation static void PropertyList_add(SystemProperty *element); static void PropertyList_add(SystemProperty** plist, SystemProperty *element); @@ -651,7 +594,6 @@ class Arguments : AllStatic { static void PropertyList_unique_add(SystemProperty** plist, const char* k, const char* v, PropertyAppendable append, PropertyWriteable writeable, PropertyInternal internal); - static void PropertyList_update_value(SystemProperty* plist, const char* k, const char* v); static const char* PropertyList_get_value(SystemProperty* plist, const char* key); static const char* PropertyList_get_readable_value(SystemProperty* plist, const char* key); static int PropertyList_count(SystemProperty* pl); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index cee154153b8..8d2b4b5f3f6 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -2071,7 +2071,7 @@ Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* thread, j // Local derived constants. // Further breakdown of DataLayout::trap_state, as promised by DataLayout. -const int DS_REASON_MASK = DataLayout::trap_mask >> 1; +const int DS_REASON_MASK = ((uint)DataLayout::trap_mask) >> 1; const int DS_RECOMPILE_BIT = DataLayout::trap_mask - DS_REASON_MASK; //---------------------------trap_state_reason--------------------------------- diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 7eb058385bf..380f3e4c1e5 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -131,6 +131,8 @@ Monitor* Service_lock = NULL; Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; +Mutex* ThreadHeapSampler_lock = NULL; + #if INCLUDE_JFR Mutex* JfrStacktrace_lock = NULL; Monitor* JfrMsg_lock = NULL; @@ -296,6 +298,9 @@ void mutex_init() { def(CompileThread_lock , PaddedMonitor, nonleaf+5, false, Monitor::_safepoint_check_always); def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes); def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always); + + def(ThreadHeapSampler_lock , PaddedMutex, nonleaf, false, Monitor::_safepoint_check_never); + if (WhiteBoxAPI) { def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never); } diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index a477899929c..723afa5951a 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -130,6 +130,7 @@ extern Mutex* Management_lock; // a lock used to serialize JVM extern Monitor* Service_lock; // a lock used for service thread operation extern Monitor* PeriodicTask_lock; // protects the periodic task structure extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition +extern Mutex* ThreadHeapSampler_lock; // protects the static data for initialization. #if INCLUDE_JFR extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 7cfdaad2c01..6ae6717f4ac 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1127,35 +1127,6 @@ static void call_postVMInitHook(TRAPS) { } } -static void reset_vm_info_property(TRAPS) { - // the vm info string - ResourceMark rm(THREAD); - const char *vm_info = VM_Version::vm_info_string(); - - // update the native system property first - Arguments::PropertyList_update_value(Arguments::system_properties(), "java.vm.info", vm_info); - - // java.lang.System class - Klass* klass = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(), true, CHECK); - - // setProperty arguments - Handle key_str = java_lang_String::create_from_str("java.vm.info", CHECK); - Handle value_str = java_lang_String::create_from_str(vm_info, CHECK); - - // return value - JavaValue r(T_OBJECT); - - // public static String setProperty(String key, String value); - JavaCalls::call_static(&r, - klass, - vmSymbols::setProperty_name(), - vmSymbols::string_string_string_signature(), - key_str, - value_str, - CHECK); -} - - void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name, bool daemon, TRAPS) { assert(thread_group.not_null(), "thread group should be specified"); @@ -3771,6 +3742,14 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { VMThread::execute(&verify_op); } + // We need this to update the java.vm.info property in case any flags used + // to initially define it have been changed. This is needed for both CDS and + // AOT, since UseSharedSpaces and UseAOT may be changed after java.vm.info + // is initially computed. See Abstract_VM_Version::vm_info_string(). + // This update must happen before we initialize the java classes, but + // after any initialization logic that might modify the flags. + Arguments::update_vm_info_property(VM_Version::vm_info_string()); + Thread* THREAD = Thread::current(); // Always call even when there are not JVMTI environments yet, since environments @@ -3782,12 +3761,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { initialize_java_lang_classes(main_thread, CHECK_JNI_ERR); - // We need this to update the java.vm.info property in case any flags used - // to initially define it have been changed. This is needed for both CDS and - // AOT, since UseSharedSpaces and UseAOT may be changed after java.vm.info - // is initially computed. See Abstract_VM_Version::vm_info_string(). - reset_vm_info_property(CHECK_JNI_ERR); - quicken_jni_functions(); // No more stub generation allowed after that point. diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 2c47c5325ba..1f0b22bb3e0 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -42,6 +42,7 @@ #include "runtime/park.hpp" #include "runtime/safepoint.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadHeapSampler.hpp" #include "runtime/threadLocalStorage.hpp" #include "runtime/unhandledOops.hpp" #include "utilities/align.hpp" @@ -338,6 +339,7 @@ class Thread: public ThreadShadow { ThreadLocalAllocBuffer _tlab; // Thread-local eden jlong _allocated_bytes; // Cumulative number of bytes allocated on // the Java heap + ThreadHeapSampler _heap_sampler; // For use when sampling the memory. JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr @@ -517,6 +519,8 @@ class Thread: public ThreadShadow { void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } inline jlong cooked_allocated_bytes(); + ThreadHeapSampler& heap_sampler() { return _heap_sampler; } + JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;) bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; } diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp new file mode 100644 index 00000000000..abe6773a7d9 --- /dev/null +++ b/src/hotspot/share/runtime/threadHeapSampler.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 "runtime/handles.inline.hpp" +#include "runtime/orderAccess.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/threadHeapSampler.hpp" + +// Cheap random number generator +uint64_t ThreadHeapSampler::_rnd; +// Default is 512kb. +int ThreadHeapSampler::_sampling_rate = 512 * 1024; +int ThreadHeapSampler::_enabled; + +// Statics for the fast log +static const int FastLogNumBits = 10; +static const int FastLogMask = (1 << FastLogNumBits) - 1; +static double log_table[1<0, "bad value passed to assert"); + uint64_t x = 0; + assert(sizeof(d) == sizeof(x), + "double and uint64_t do not have the same size"); + x = *reinterpret_cast(&d); + const uint32_t x_high = x >> 32; + assert(FastLogNumBits <= 20, "FastLogNumBits should be less than 20."); + const uint32_t y = x_high >> (20 - FastLogNumBits) & FastLogMask; + const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023; + return exponent + log_table[y]; +} + +// Generates a geometric variable with the specified mean (512K by default). +// This is done by generating a random number between 0 and 1 and applying +// the inverse cumulative distribution function for an exponential. +// Specifically: Let m be the inverse of the sample rate, then +// the probability distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +void ThreadHeapSampler::pick_next_geometric_sample() { + _rnd = next_random(_rnd); + // Take the top 26 bits as the random number + // (This plus a 1<<58 sampling bound gives a max possible step of + // 5194297183973780480 bytes. In this case, + // for sample_parameter = 1<<19, max possible step is + // 9448372 bytes (24 bits). + const uint64_t PrngModPower = 48; // Number of bits in prng + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(_rnd >> (PrngModPower - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + // For faster performance (save ~1/20th exec time), replace + // min(0.0, FastLog2(q) - 26) by (Fastlog2(q) - 26.000705) + // The value 26.000705 is used rather than 26 to compensate + // for inaccuracies in FastLog2 which otherwise result in a + // negative answer. + double log_val = (fast_log2(q) - 26); + double result = + (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_rate())) + 1; + assert(result > 0 && result < SIZE_MAX, "Result is not in an acceptable range."); + size_t rate = static_cast(result); + _bytes_until_sample = rate; +} + +void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) { + if (get_sampling_rate() == 1) { + _bytes_until_sample = 1; + return; + } + + pick_next_geometric_sample(); + + // Try to correct sample size by removing extra space from last allocation. + if (overflowed_bytes > 0 && _bytes_until_sample > overflowed_bytes) { + _bytes_until_sample -= overflowed_bytes; + } +} + +void ThreadHeapSampler::check_for_sampling(HeapWord* ptr, size_t allocation_size, size_t bytes_since_allocation) { + oopDesc* oop = reinterpret_cast(ptr); + size_t total_allocated_bytes = bytes_since_allocation + allocation_size; + + // If not yet time for a sample, skip it. + if (total_allocated_bytes < _bytes_until_sample) { + _bytes_until_sample -= total_allocated_bytes; + return; + } + + JvmtiExport::sampled_object_alloc_event_collector(oop); + + size_t overflow_bytes = total_allocated_bytes - _bytes_until_sample; + pick_next_sample(overflow_bytes); +} + +void ThreadHeapSampler::init_log_table() { + MutexLockerEx mu(ThreadHeapSampler_lock, Mutex::_no_safepoint_check_flag); + + if (log_table_initialized) { + return; + } + + for (int i = 0; i < (1 << FastLogNumBits); i++) { + log_table[i] = (log(1.0 + static_cast(i+0.5) / (1 << FastLogNumBits)) + / log(2.0)); + } + + log_table_initialized = true; +} + +void ThreadHeapSampler::enable() { + // Done here to be done when things have settled. This adds a mutex lock but + // presumably, users won't be enabling and disabling all the time. + init_log_table(); + OrderAccess::release_store(&_enabled, 1); +} + +int ThreadHeapSampler::enabled() { + return OrderAccess::load_acquire(&_enabled); +} + +void ThreadHeapSampler::disable() { + OrderAccess::release_store(&_enabled, 0); +} + +int ThreadHeapSampler::get_sampling_rate() { + return OrderAccess::load_acquire(&_sampling_rate); +} + +void ThreadHeapSampler::set_sampling_rate(int sampling_rate) { + OrderAccess::release_store(&_sampling_rate, sampling_rate); +} + +// Methods used in assertion mode to check if a collector is present or not at +// the moment of TLAB sampling, ie a slow allocation path. +bool ThreadHeapSampler::sampling_collector_present() const { + return _collectors_present > 0; +} + +bool ThreadHeapSampler::remove_sampling_collector() { + assert(_collectors_present > 0, "Problem with collector counter."); + _collectors_present--; + return true; +} + +bool ThreadHeapSampler::add_sampling_collector() { + _collectors_present++; + return true; +} diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp new file mode 100644 index 00000000000..22d55bd4069 --- /dev/null +++ b/src/hotspot/share/runtime/threadHeapSampler.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 RUNTIME_THREADHEAPSAMPLER_HPP +#define RUNTIME_THREADHEAPSAMPLER_HPP + +#include "memory/allocation.hpp" + +class ThreadHeapSampler { + private: + size_t _bytes_until_sample; + // Cheap random number generator + static uint64_t _rnd; + + void pick_next_geometric_sample(); + void pick_next_sample(size_t overflowed_bytes = 0); + static int _enabled; + static int _sampling_rate; + + // Used for assertion mode to determine if there is a path to a TLAB slow path + // without a collector present. + size_t _collectors_present; + + static void init_log_table(); + + public: + ThreadHeapSampler() : _bytes_until_sample(0) { + _rnd = static_cast(reinterpret_cast(this)); + if (_rnd == 0) { + _rnd = 1; + } + + _collectors_present = 0; + } + + size_t bytes_until_sample() { return _bytes_until_sample; } + void set_bytes_until_sample(size_t bytes) { _bytes_until_sample = bytes; } + + void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0); + + static int enabled(); + static void enable(); + static void disable(); + + static void set_sampling_rate(int sampling_rate); + static int get_sampling_rate(); + + bool sampling_collector_present() const; + bool remove_sampling_collector(); + bool add_sampling_collector(); +}; + +#endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 1956a6ef983..3406d558a6a 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -291,6 +291,7 @@ typedef PaddedEnd PaddedObjectMonitor; nonstatic_field(DataLayout, _header._struct._tag, u1) \ nonstatic_field(DataLayout, _header._struct._flags, u1) \ nonstatic_field(DataLayout, _header._struct._bci, u2) \ + nonstatic_field(DataLayout, _header._struct._traps, u4) \ nonstatic_field(DataLayout, _cells[0], intptr_t) \ nonstatic_field(MethodCounters, _nmethod_age, int) \ nonstatic_field(MethodCounters, _interpreter_invocation_limit, int) \ diff --git a/src/hotspot/share/services/memTracker.hpp b/src/hotspot/share/services/memTracker.hpp index 0a4cf68bbc2..3676f54e73a 100644 --- a/src/hotspot/share/services/memTracker.hpp +++ b/src/hotspot/share/services/memTracker.hpp @@ -241,6 +241,11 @@ class MemTracker : AllStatic { } } +#ifdef _AIX + // See JDK-8202772 - temporarily disable thread stack tracking on AIX. + static inline void record_thread_stack(void* addr, size_t size) {} + static inline void release_thread_stack(void* addr, size_t size) {} +#else static inline void record_thread_stack(void* addr, size_t size) { if (tracking_level() < NMT_summary) return; if (addr != NULL) { @@ -260,6 +265,7 @@ class MemTracker : AllStatic { VirtualMemoryTracker::remove_released_region((address)addr, size); } } +#endif // Query lock is used to synchronize the access to tracking data. // So far, it is only used by JCmd query, but it may be used by diff --git a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java new file mode 100644 index 00000000000..7d6c0b7bdd9 --- /dev/null +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java @@ -0,0 +1,461 @@ +/* + * 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. 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.internal.platform.cgroupv1; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +public class Metrics implements jdk.internal.platform.Metrics { + private SubSystem memory; + private SubSystem cpu; + private SubSystem cpuacct; + private SubSystem cpuset; + private SubSystem blkio; + private boolean activeSubSystems; + + // Values returned larger than this number are unlimited. + static long unlimited_minimum = 0x7FFFFFFFFF000000L; + + private static final Metrics INSTANCE = initContainerSubSystems(); + + private static final String PROVIDER_NAME = "cgroupv1"; + + private Metrics() { + activeSubSystems = false; + } + + public static Metrics getInstance() { + return INSTANCE; + } + + private static Metrics initContainerSubSystems() { + Metrics metrics = new Metrics(); + + /** + * Find the cgroup mount points for subsystems + * by reading /proc/self/mountinfo + * + * Example for docker MemorySubSystem subsystem: + * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem + * + * Example for host: + * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem + */ + try (Stream lines = + Files.lines(Paths.get("/proc/self/mountinfo"))) { + + lines.filter(line -> line.contains(" - cgroup ")) + .map(line -> line.split(" ")) + .forEach(entry -> createSubSystem(metrics, entry)); + + } catch (IOException e) { + return null; + } + + /** + * Read /proc/self/cgroup and map host mount point to + * local one via /proc/self/mountinfo content above + * + * Docker example: + * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 + * + * Host example: + * 5:memory:/user.slice + * + * Construct a path to the process specific memory and cpuset + * cgroup directory. + * + * For a container running under Docker from memory example above + * the paths would be: + * + * /sys/fs/cgroup/memory + * + * For a Host from memory example above the path would be: + * + * /sys/fs/cgroup/memory/user.slice + * + */ + try (Stream lines = + Files.lines(Paths.get("/proc/self/cgroup"))) { + + lines.map(line -> line.split(":")) + .filter(line -> (line.length >= 3)) + .forEach(line -> setSubSystemPath(metrics, line)); + + } catch (IOException e) { + return null; + } + + // Return Metrics object if we found any subsystems. + if (metrics.activeSubSystems()) { + return metrics; + } + + return null; + } + + /** + * createSubSystem objects and initialize mount points + */ + private static void createSubSystem(Metrics metric, String [] mountentry) { + if (mountentry.length < 5) return; + + Path p = Paths.get(mountentry[4]); + String subsystemName = p.getFileName().toString(); + + if (subsystemName != null) { + switch (subsystemName) { + case "memory": + metric.setMemorySubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpuset": + metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpu,cpuacct": + case "cpuacct,cpu": + metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); + metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpuacct": + metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpu": + metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "blkio": + metric.setBlkIOSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + default: + // Ignore subsystems that we don't support + break; + } + } + } + + /** + * setSubSystemPath based on the contents of /proc/self/cgroup + */ + private static void setSubSystemPath(Metrics metric, String [] entry) { + String controller; + String base; + SubSystem subsystem = null; + SubSystem subsystem2 = null; + + controller = entry[1]; + base = entry[2]; + if (controller != null && base != null) { + switch (controller) { + case "memory": + subsystem = metric.MemorySubSystem(); + break; + case "cpuset": + subsystem = metric.CpuSetSubSystem(); + break; + case "cpu,cpuacct": + case "cpuacct,cpu": + subsystem = metric.CpuSubSystem(); + subsystem2 = metric.CpuAcctSubSystem(); + break; + case "cpuacct": + subsystem = metric.CpuAcctSubSystem(); + break; + case "cpu": + subsystem = metric.CpuSubSystem(); + break; + case "blkio": + subsystem = metric.BlkIOSubSystem(); + break; + // Ignore subsystems that we don't support + default: + break; + } + } + + if (subsystem != null) { + subsystem.setPath(base); + metric.setActiveSubSystems(); + } + if (subsystem2 != null) { + subsystem2.setPath(base); + } + } + + + private void setActiveSubSystems() { + activeSubSystems = true; + } + + private boolean activeSubSystems() { + return activeSubSystems; + } + + private void setMemorySubSystem(SubSystem memory) { + this.memory = memory; + } + + private void setCpuSubSystem(SubSystem cpu) { + this.cpu = cpu; + } + + private void setCpuAcctSubSystem(SubSystem cpuacct) { + this.cpuacct = cpuacct; + } + + private void setCpuSetSubSystem(SubSystem cpuset) { + this.cpuset = cpuset; + } + + private void setBlkIOSubSystem(SubSystem blkio) { + this.blkio = blkio; + } + + private SubSystem MemorySubSystem() { + return memory; + } + + private SubSystem CpuSubSystem() { + return cpu; + } + + private SubSystem CpuAcctSubSystem() { + return cpuacct; + } + + private SubSystem CpuSetSubSystem() { + return cpuset; + } + + private SubSystem BlkIOSubSystem() { + return blkio; + } + + public String getProvider() { + return PROVIDER_NAME; + } + + /***************************************************************** + * CPU Accounting Subsystem + ****************************************************************/ + + + public long getCpuUsage() { + return SubSystem.getLongValue(cpuacct, "cpuacct.usage"); + } + + public long[] getPerCpuUsage() { + String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu"); + if (usagelist == null) { + return new long[0]; + } + + String list[] = usagelist.split(" "); + long percpu[] = new long[list.length]; + for (int i = 0; i < list.length; i++) { + percpu[i] = Long.parseLong(list[i]); + } + return percpu; + } + + public long getCpuUserUsage() { + return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user"); + } + + public long getCpuSystemUsage() { + return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "system"); + } + + + /***************************************************************** + * CPU Subsystem + ****************************************************************/ + + + public long getCpuPeriod() { + return SubSystem.getLongValue(cpuacct, "cpu.cfs_period_us"); + } + + public long getCpuQuota() { + return SubSystem.getLongValue(cpuacct, "cpu.cfs_quota_us"); + } + + public long getCpuShares() { + long retval = SubSystem.getLongValue(cpuacct, "cpu.shares"); + if (retval == 0 || retval == 1024) + return -1; + else + return retval; + } + + public long getCpuNumPeriods() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "nr_periods"); + } + + public long getCpuNumThrottled() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "nr_throttled"); + } + + public long getCpuThrottledTime() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "throttled_time"); + } + + public long getEffectiveCpuCount() { + return Runtime.getRuntime().availableProcessors(); + } + + + /***************************************************************** + * CPUSet Subsystem + ****************************************************************/ + + public int[] getCpuSetCpus() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus")); + } + + public int[] getEffectiveCpuSetCpus() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus")); + } + + public int[] getCpuSetMems() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems")); + } + + public int[] getEffectiveCpuSetMems() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems")); + } + + public double getCpuSetMemoryPressure() { + return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure"); + } + + public boolean isCpuSetMemoryPressureEnabled() { + long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled"); + return (val == 1); + } + + + /***************************************************************** + * Memory Subsystem + ****************************************************************/ + + + public long getMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.failcnt"); + } + + public long getMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes"); + } + + public long getMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.usage_in_bytes"); + } + + public long getKernelMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.kmem.failcnt"); + } + + public long getKernelMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getKernelMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.max_usage_in_bytes"); + } + + public long getKernelMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes"); + } + + public long getTcpMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt"); + } + + public long getTcpMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getTcpMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); + } + + public long getTcpMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); + } + + public long getMemoryAndSwapFailCount() { + return SubSystem.getLongValue(memory, "memory.memsw.failcnt"); + } + + public long getMemoryAndSwapLimit() { + long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getMemoryAndSwapMaxUsage() { + return SubSystem.getLongValue(memory, "memory.memsw.max_usage_in_bytes"); + } + + public long getMemoryAndSwapUsage() { + return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes"); + } + + public boolean isMemoryOOMKillEnabled() { + long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); + return (val == 0); + } + + public long getMemorySoftLimit() { + long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + + /***************************************************************** + * BlKIO Subsystem + ****************************************************************/ + + + public long getBlkIOServiceCount() { + return SubSystem.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); + } + + public long getBlkIOServiced() { + return SubSystem.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); + } + +} diff --git a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java new file mode 100644 index 00000000000..b010a34df08 --- /dev/null +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java @@ -0,0 +1,208 @@ +/* + * 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. 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.internal.platform.cgroupv1; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Optional; +import java.util.stream.Stream; + +public class SubSystem { + String root; + String mountPoint; + String path; + + public SubSystem(String root, String mountPoint) { + this.root = root; + this.mountPoint = mountPoint; + } + + public void setPath(String cgroupPath) { + if (root != null && cgroupPath != null) { + if (root.equals("/")) { + if (cgroupPath.equals("/")) { + path = mountPoint + cgroupPath; + } + else { + path = mountPoint; + } + } + else { + if (root.equals(cgroupPath)) { + path = mountPoint; + } + else { + if (root.indexOf(cgroupPath) == 0) { + if (cgroupPath.length() > root.length()) { + String cgroupSubstr = cgroupPath.substring(root.length()); + path = mountPoint + cgroupSubstr; + } + } + } + } + } + } + + public String path() { + return path; + } + + /** + * getSubSystemStringValue + * + * Return the first line of the file "parm" argument from the subsystem. + * + * TODO: Consider using weak references for caching BufferedReader object. + * + * @param subsystem + * @param parm + * @return Returns the contents of the file specified by param. + */ + public static String getStringValue(SubSystem subsystem, String parm) { + if (subsystem == null) return null; + + try(BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(subsystem.path(), parm))) { + String line = bufferedReader.readLine(); + return line; + } + catch (IOException e) { + return null; + } + + } + + public static long getLongValue(SubSystem subsystem, String parm) { + String strval = getStringValue(subsystem, parm); + + if (strval == null) return 0L; + + long retval = Long.parseLong(strval); + + return retval; + } + + public static double getDoubleValue(SubSystem subsystem, String parm) { + String strval = getStringValue(subsystem, parm); + + if (strval == null) return 0L; + + double retval = Double.parseDouble(strval); + + return retval; + } + + /** + * getSubSystemlongEntry + * + * Return the long value from the line containing the string "entryname" + * within file "parm" in the "subsystem". + * + * TODO: Consider using weak references for caching BufferedReader object. + * + * @param subsystem + * @param parm + * @param entryname + * @return long value + */ + public static long getLongEntry(SubSystem subsystem, String parm, String entryname) { + String val = null; + + if (subsystem == null) return 0L; + + try (Stream lines = Files.lines(Paths.get(subsystem.path(), parm))) { + + Optional result = lines.map(line -> line.split(" ")) + .filter(line -> (line.length == 2 && + line[0].equals(entryname))) + .map(line -> line[1]) + .findFirst(); + + return result.isPresent() ? Long.parseLong(result.get()) : 0L; + } + catch (IOException e) { + return 0L; + } + } + + public static int getIntValue(SubSystem subsystem, String parm) { + String val = getStringValue(subsystem, parm); + + if (val == null) return 0; + + return Integer.parseInt(val); + } + + /** + * StringRangeToIntArray + * + * Convert a string in the form of 1,3-4,6 to an array of + * integers containing all the numbers in the range. + * + * @param range + * @return int[] containing a sorted list of processors or memory nodes + */ + public static int[] StringRangeToIntArray(String range) { + int[] ints = new int[0]; + + if (range == null) return ints; + + ArrayList results = new ArrayList<>(); + String strs[] = range.split(","); + for (String str : strs) { + if (str.contains("-")) { + String lohi[] = str.split("-"); + // validate format + if (lohi.length != 2) { + continue; + } + int lo = Integer.parseInt(lohi[0]); + int hi = Integer.parseInt(lohi[1]); + for (int i = lo; i <= hi; i++) { + results.add(i); + } + } + else { + results.add(Integer.parseInt(str)); + } + } + + // sort results + results.sort(null); + + // convert ArrayList to primitive int array + ints = new int[results.size()]; + int i = 0; + for (Integer n : results) { + ints[i++] = n; + } + + return ints; + } +} diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 551823c437a..14d9e90b83f 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -820,7 +820,7 @@ public final class Class implements java.io.Serializable, * primitive type or void, then the {@code Module} object for the * {@code java.base} module is returned. * - * If this class is in an unnamed module then the {@link + * If this class is in an unnamed module then the {@linkplain * ClassLoader#getUnnamedModule() unnamed} {@code Module} of the class * loader for this class is returned. * @@ -953,14 +953,14 @@ public final class Class implements java.io.Serializable, * empty string if the class is in an unnamed package. * *

If this class is a member class, then this method is equivalent to - * invoking {@code getPackageName()} on the {@link #getEnclosingClass + * invoking {@code getPackageName()} on the {@linkplain #getEnclosingClass * enclosing class}. * - *

If this class is a {@link #isLocalClass local class} or an {@link + *

If this class is a {@linkplain #isLocalClass local class} or an {@linkplain * #isAnonymousClass() anonymous class}, then this method is equivalent to - * invoking {@code getPackageName()} on the {@link #getDeclaringClass - * declaring class} of the {@link #getEnclosingMethod enclosing method} or - * {@link #getEnclosingConstructor enclosing constructor}. + * invoking {@code getPackageName()} on the {@linkplain #getDeclaringClass + * declaring class} of the {@linkplain #getEnclosingMethod enclosing method} or + * {@linkplain #getEnclosingConstructor enclosing constructor}. * *

If this class represents an array type then this method returns the * package name of the element type. If this class represents a primitive @@ -2576,7 +2576,7 @@ public final class Class implements java.io.Serializable, * @param name name of the desired resource * @return A {@link java.io.InputStream} object; {@code null} if no * resource with this name is found, the resource is in a package - * that is not {@link Module#isOpen(String, Module) open} to at + * that is not {@linkplain Module#isOpen(String, Module) open} to at * least the caller module, or access to the resource is denied * by the security manager. * @throws NullPointerException If {@code name} is {@code null} @@ -2675,7 +2675,7 @@ public final class Class implements java.io.Serializable, * @return A {@link java.net.URL} object; {@code null} if no resource with * this name is found, the resource cannot be located by a URL, the * resource is in a package that is not - * {@link Module#isOpen(String, Module) open} to at least the caller + * {@linkplain Module#isOpen(String, Module) open} to at least the caller * module, or access to the resource is denied by the security * manager. * @throws NullPointerException If {@code name} is {@code null} diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index 77e142dbffd..d121a802638 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -589,6 +589,10 @@ class StringCoding { } private static byte[] encode8859_1(byte coder, byte[] val) { + return encode8859_1(coder, val, true); + } + + private static byte[] encode8859_1(byte coder, byte[] val, boolean doReplace) { if (coder == LATIN1) { return Arrays.copyOf(val, val.length); } @@ -602,6 +606,9 @@ class StringCoding { sp = sp + ret; dp = dp + ret; if (ret != len) { + if (!doReplace) { + throwMalformed(sp, 1); + } char c = StringUTF16.getChar(val, sp++); if (Character.isHighSurrogate(c) && sp < sl && Character.isLowSurrogate(StringUTF16.getChar(val, sp))) { @@ -676,6 +683,12 @@ class StringCoding { ", length : " + nb); } + private static void throwMalformed(byte[] val) { + int dp = 0; + while (dp < val.length && val[dp] >=0) { dp++; } + throwMalformed(dp, 1); + } + private static char repl = '\ufffd'; private static Result decodeUTF8(byte[] src, int sp, int len, boolean doReplace) { @@ -931,7 +944,7 @@ class StringCoding { ////////////////////// for j.u.z.ZipCoder ////////////////////////// /* - * Throws iae, instead of replacing, if malformed or unmappble. + * Throws iae, instead of replacing, if malformed or unmappable. */ static String newStringUTF8NoRepl(byte[] src, int off, int len) { if (COMPACT_STRINGS && !hasNegatives(src, off, len)) @@ -941,9 +954,137 @@ class StringCoding { } /* - * Throws iae, instead of replacing, if unmappble. + * Throws iae, instead of replacing, if unmappable. */ static byte[] getBytesUTF8NoRepl(String s) { return encodeUTF8(s.coder(), s.value(), false); } + + ////////////////////// for j.n.f.Files ////////////////////////// + + private static boolean isASCII(byte[] src) { + return !hasNegatives(src, 0, src.length); + } + + private static String newStringLatin1(byte[] src) { + if (COMPACT_STRINGS) + return new String(src, LATIN1); + return new String(StringLatin1.inflate(src, 0, src.length), UTF16); + } + + static String newStringNoRepl(byte[] src, Charset cs) { + if (cs == UTF_8) { + if (COMPACT_STRINGS && isASCII(src)) + return new String(src, LATIN1); + Result ret = decodeUTF8_0(src, 0, src.length, false); + return new String(ret.value, ret.coder); + } + if (cs == ISO_8859_1) { + return newStringLatin1(src); + } + if (cs == US_ASCII) { + if (isASCII(src)) { + return newStringLatin1(src); + } else { + throwMalformed(src); + } + } + + CharsetDecoder cd = cs.newDecoder(); + // ascii fastpath + if ((cd instanceof ArrayDecoder) && + ((ArrayDecoder)cd).isASCIICompatible() && isASCII(src)) { + return newStringLatin1(src); + } + int len = src.length; + if (len == 0) { + return ""; + } + int en = scale(len, cd.maxCharsPerByte()); + char[] ca = new char[en]; + if (cs.getClass().getClassLoader0() != null && + System.getSecurityManager() != null) { + src = Arrays.copyOf(src, len); + } + ByteBuffer bb = ByteBuffer.wrap(src); + CharBuffer cb = CharBuffer.wrap(ca); + try { + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + throw new IllegalArgumentException(x); // todo + } + Result ret = resultCached.get().with(ca, 0, cb.position()); + return new String(ret.value, ret.coder); + } + + /* + * Throws iae, instead of replacing, if unmappable. + */ + static byte[] getBytesNoRepl(String s, Charset cs) { + byte[] val = s.value(); + byte coder = s.coder(); + if (cs == UTF_8) { + if (isASCII(val)) { + return val; + } + return encodeUTF8(coder, val, false); + } + if (cs == ISO_8859_1) { + if (coder == LATIN1) { + return val; + } + return encode8859_1(coder, val, false); + } + if (cs == US_ASCII) { + if (coder == LATIN1) { + if (isASCII(val)) { + return val; + } else { + throwMalformed(val); + } + } + } + CharsetEncoder ce = cs.newEncoder(); + // fastpath for ascii compatible + if (coder == LATIN1 && (((ce instanceof ArrayEncoder) && + ((ArrayEncoder)ce).isASCIICompatible() && + isASCII(val)))) { + return val; + } + int len = val.length >> coder; // assume LATIN1=0/UTF16=1; + int en = scale(len, ce.maxBytesPerChar()); + byte[] ba = new byte[en]; + if (len == 0) { + return ba; + } + if (ce instanceof ArrayEncoder) { + int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba) + : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba); + if (blen != -1) { + return safeTrim(ba, blen, true); + } + } + boolean isTrusted = cs.getClass().getClassLoader0() == null || + System.getSecurityManager() == null; + char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val) + : StringUTF16.toChars(val); + ByteBuffer bb = ByteBuffer.wrap(ba); + CharBuffer cb = CharBuffer.wrap(ca, 0, len); + try { + CoderResult cr = ce.encode(cb, bb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = ce.flush(bb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + throw new Error(x); + } + return safeTrim(ba, bb.position(), isTrusted); + } } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index f1215fbf303..d775b9aecc3 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -47,6 +47,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.nio.channels.Channel; import java.nio.channels.spi.SelectorProvider; +import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -2152,6 +2153,14 @@ public final class System { return ModuleLayer.layers(loader); } + public String newStringNoRepl(byte[] bytes, Charset cs) { + return StringCoding.newStringNoRepl(bytes, cs); + } + + public byte[] getBytesNoRepl(String s, Charset cs) { + return StringCoding.getBytesNoRepl(s, cs); + } + public String newStringUTF8NoRepl(byte[] bytes, int off, int len) { return StringCoding.newStringUTF8NoRepl(bytes, off, len); } diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index 82c2c376af4..231c994e20d 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -1353,6 +1353,38 @@ public abstract class $Type$Buffer #end[floatingPointType] } + /** + * Finds and returns the relative index of the first mismatch between this + * buffer and a given buffer. The index is relative to the + * {@link #position() position} of each buffer and will be in the range of + * 0 (inclusive) up to the smaller of the {@link #remaining() remaining} + * elements in each buffer (exclusive). + * + *

If the two buffers share a common prefix then the returned index is + * the length of the common prefix and it follows that there is a mismatch + * between the two buffers at that index within the respective buffers. + * If one buffer is a proper prefix of the other then the returned index is + * the smaller of the remaining elements in each buffer, and it follows that + * the index is only valid for the buffer with the larger number of + * remaining elements. + * Otherwise, there is no mismatch. + * + * @param that + * The byte buffer to be tested for a mismatch with this buffer + * + * @return The relative index of the first mismatch between this and the + * given buffer, otherwise -1 if no mismatch. + * + * @since 11 + */ + public int mismatch($Type$Buffer that) { + int length = Math.min(this.remaining(), that.remaining()); + int r = BufferMismatch.mismatch(this, this.position(), + that, that.position(), + length); + return (r == -1 && this.remaining() != that.remaining()) ? length : r; + } + // -- Other char stuff -- #if[char] diff --git a/src/java.base/share/classes/java/nio/file/Files.java b/src/java.base/share/classes/java/nio/file/Files.java index 65fb4a5f2bf..348e0555dd5 100644 --- a/src/java.base/share/classes/java/nio/file/Files.java +++ b/src/java.base/share/classes/java/nio/file/Files.java @@ -3121,6 +3121,9 @@ public final class Files { */ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + private static final jdk.internal.misc.JavaLangAccess JLA = + jdk.internal.misc.SharedSecrets.getJavaLangAccess(); + /** * Reads all the bytes from an input stream. Uses {@code initialSize} as a hint * about how many bytes the stream will have. @@ -3202,6 +3205,81 @@ public final class Files { } } + /** + * Reads all content from a file into a string, decoding from bytes to characters + * using the {@link StandardCharsets#UTF_8 UTF-8} {@link Charset charset}. + * The method ensures that the file is closed when all content have been read + * or an I/O error, or other runtime exception, is thrown. + * + *

This method is equivalent to: + * {@code readString(path, StandardCharsets.UTF_8) } + * + * @param path the path to the file + * + * @return a String containing the content read from the file + * + * @throws IOException + * if an I/O error occurs reading from the file or a malformed or + * unmappable byte sequence is read + * @throws OutOfMemoryError + * if the file is extremely large, for example larger than {@code 2GB} + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkRead(String) checkRead} + * method is invoked to check read access to the file. + * + * @since 11 + */ + public static String readString(Path path) throws IOException { + return readString(path, StandardCharsets.UTF_8); + } + + /** + * Reads all characters from a file into a string, decoding from bytes to characters + * using the specified {@linkplain Charset charset}. + * The method ensures that the file is closed when all content have been read + * or an I/O error, or other runtime exception, is thrown. + * + *

This method reads all content including the line separators in the middle + * and/or at the end. The resulting string will contain line separators as they + * appear in the file. + * + * @apiNote + * This method is intended for simple cases where it is appropriate and convenient + * to read the content of a file into a String. It is not intended for reading + * very large files. + * + * + * + * @param path the path to the file + * @param cs the charset to use for decoding + * + * @return a String containing the content read from the file + * + * @throws IOException + * if an I/O error occurs reading from the file or a malformed or + * unmappable byte sequence is read + * @throws OutOfMemoryError + * if the file is extremely large, for example larger than {@code 2GB} + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkRead(String) checkRead} + * method is invoked to check read access to the file. + * + * @since 11 + */ + public static String readString(Path path, Charset cs) throws IOException { + Objects.requireNonNull(path); + Objects.requireNonNull(cs); + + byte[] ba = readAllBytes(path); + try { + return JLA.newStringNoRepl(ba, cs); + } catch (IllegalArgumentException e) { + throw new IOException(e); + } + } + /** * Read all lines from a file. This method ensures that the file is * closed when all bytes have been read or an I/O error, or other runtime @@ -3456,6 +3534,110 @@ public final class Files { return write(path, lines, StandardCharsets.UTF_8, options); } + /** + * Write a {@linkplain java.lang.CharSequence CharSequence} to a file. + * Characters are encoded into bytes using the + * {@link StandardCharsets#UTF_8 UTF-8} {@link Charset charset}. + * + *

This method is equivalent to: + * {@code writeString(path, test, StandardCharsets.UTF_8, options) } + * + * @param path + * the path to the file + * @param csq + * the CharSequence to be written + * @param options + * options specifying how the file is opened + * + * @return the path + * + * @throws IllegalArgumentException + * if {@code options} contains an invalid combination of options + * @throws IOException + * if an I/O error occurs writing to or creating the file, or the + * text cannot be encoded using the specified charset + * @throws UnsupportedOperationException + * if an unsupported option is specified + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkWrite(String) checkWrite} + * method is invoked to check 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. + * + * @since 11 + */ + public static Path writeString(Path path, CharSequence csq, OpenOption... options) + throws IOException + { + return writeString(path, csq, StandardCharsets.UTF_8, options); + } + + /** + * Write a {@linkplain java.lang.CharSequence CharSequence} to a file. + * Characters are encoded into bytes using the specified + * {@linkplain java.nio.charset.Charset charset}. + * + *

All characters are written as they are, including the line separators in + * the char sequence. No extra characters are added. + * + *

The {@code options} parameter specifies how the file is created + * or opened. If no options are present then this method works as if the + * {@link StandardOpenOption#CREATE CREATE}, {@link + * StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING}, and {@link + * StandardOpenOption#WRITE WRITE} options are present. In other words, it + * opens the file for writing, creating the file if it doesn't exist, or + * initially truncating an existing {@link #isRegularFile regular-file} to + * a size of {@code 0}. + * + * + * @param path + * the path to the file + * @param csq + * the CharSequence to be written + * @param cs + * the charset to use for encoding + * @param options + * options specifying how the file is opened + * + * @return the path + * + * @throws IllegalArgumentException + * if {@code options} contains an invalid combination of options + * @throws IOException + * if an I/O error occurs writing to or creating the file, or the + * text cannot be encoded using the specified charset + * @throws UnsupportedOperationException + * if an unsupported option is specified + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkWrite(String) checkWrite} + * method is invoked to check 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. + * + * @since 11 + */ + public static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options) + throws IOException + { + // ensure the text is not null before opening file + Objects.requireNonNull(path); + Objects.requireNonNull(csq); + Objects.requireNonNull(cs); + + try { + byte[] bytes = JLA.getBytesNoRepl(String.valueOf(csq), cs); + write(path, bytes, options); + } catch (IllegalArgumentException e) { + throw new IOException(e); + } + + return path; + } + // -- Stream APIs -- /** diff --git a/src/java.base/share/classes/java/util/function/Predicate.java b/src/java.base/share/classes/java/util/function/Predicate.java index 7a0bde90150..b34febbd613 100644 --- a/src/java.base/share/classes/java/util/function/Predicate.java +++ b/src/java.base/share/classes/java/util/function/Predicate.java @@ -119,6 +119,8 @@ public interface Predicate { /** * Returns a predicate that is the negation of the supplied predicate. + * This is accomplished by returning result of the calling + * {@code target.negate()}. * * @param the type of arguments to the specified predicate * @param target predicate to negate @@ -126,10 +128,13 @@ public interface Predicate { * @return a predicate that negates the results of the supplied * predicate * + * @throws NullPointerException if target is null + * * @since 11 */ @SuppressWarnings("unchecked") static Predicate not(Predicate target) { + Objects.requireNonNull(target); return (Predicate)target.negate(); } } 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 a88482b2dbb..3fc7e7ea3eb 100644 --- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java @@ -30,6 +30,7 @@ import java.lang.module.ModuleDescriptor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.net.URI; +import java.nio.charset.Charset; import java.security.AccessControlContext; import java.security.ProtectionDomain; import java.util.Iterator; @@ -255,6 +256,36 @@ public interface JavaLangAccess { */ Stream layers(ClassLoader loader); + /** + * Constructs a new {@code String} by decoding the specified subarray of + * bytes using the specified {@linkplain java.nio.charset.Charset charset}. + * + * The caller of this method shall relinquish and transfer the ownership of + * the byte array to the callee since the later will not make a copy. + * + * @param bytes the byte array source + * @param cs the Charset + * @return the newly created string + * @throws IllegalArgumentException for malformed or unmappable bytes + */ + String newStringNoRepl(byte[] bytes, Charset cs); + + /** + * Encode the given string into a sequence of bytes using the specified Charset. + * + * This method avoids copying the String's internal representation if the input + * is ASCII. + * + * This method throws IllegalArgumentException instead of replacing when + * malformed input or unmappable characters are encountered. + * + * @param s the string to encode + * @param cs the charset + * @return the encoded bytes + * @throws IllegalArgumentException for malformed input or unmappable characters + */ + byte[] getBytesNoRepl(String s, Charset cs); + /** * Returns a new string by decoding from the given utf8 bytes array. * diff --git a/src/java.base/share/classes/jdk/internal/platform/Container.java b/src/java.base/share/classes/jdk/internal/platform/Container.java new file mode 100644 index 00000000000..9373abc30f1 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/platform/Container.java @@ -0,0 +1,45 @@ +/* + * 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. 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.internal.platform; + +/* + * @author bobv + * @since 11 + */ + +public class Container { + + private Container() { } + + /** + * Returns the platform specific Container Metrics class or + * null if not supported on this platform. + * + * @return Metrics instance or null if not supported + */ + public static Metrics metrics() { + return Metrics.systemMetrics(); + } +} diff --git a/src/java.base/share/classes/jdk/internal/platform/Metrics.java b/src/java.base/share/classes/jdk/internal/platform/Metrics.java new file mode 100644 index 00000000000..9dbafce9bc1 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/platform/Metrics.java @@ -0,0 +1,508 @@ +/* + * 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. 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.internal.platform; + +import java.lang.reflect.Method; + +/** + * Operating System Metrics class + * + * @implNote Some of the APIs within this class return metrics for an + * "Isolation Group" or "Container". When the term "Isolation Group" + * is used in the API description, this refers to either: + * + *

    + *
  1. All processes, including the current process within a container. + * + *
  2. All processes, including the current process running together + * isolated from other non-isolated processes. + * + *
  3. All processes running on a host when that there is no isolation + * in effect. + *
+ * + * @author bobv + * @since 11 + */ + +public interface Metrics { + + /** + * Returns an instance of the Metrics class. + * + * @return Metrics object or null if not supported on this platform. + */ + public static Metrics systemMetrics() { + try { + // We currently only support cgroupv1 + Class c = Class.forName("jdk.internal.platform.cgroupv1.Metrics"); + @SuppressWarnings("unchecked") + Method m = c.getMethod("getInstance"); + return (Metrics) m.invoke(null); + } catch (ClassNotFoundException e) { + return null; + } catch (ReflectiveOperationException e) { + throw new InternalError(e); + } + } + + /** + * Returns the interface responsible for providing the + * platform metrics. + * + * @implNote + * Metrics are currently only supported Linux. + * The provider for Linux is cgroupsv1. + * + * @return The name of the provider. + * + */ + public String getProvider(); + + + /***************************************************************** + * CPU Accounting Subsystem + ****************************************************************/ + + /** + * Returns the aggregate time, in nanoseconds, consumed by all + * tasks in the Isolation Group. + * + * @return Time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuUsage(); + + /** + * Returns the aggregate time, in nanoseconds, consumed by all tasks in + * the Isolation Group, separated by CPU. If the current process + * is running within a container, the reported time will only be + * valid for processes running within the same container. The values + * are returned in an array, one entry for each physical processor + * on the system. Time values for processors unavailable to this + * Group are undefined. + * + * @return long array of time values. The size of the array is equal + * to the total number of physical processors in the system. If + * this metric is not available, a zero length array will be + * returned. + * + */ + public long[] getPerCpuUsage(); + + /** + * Returns the aggregate user time, in nanoseconds, consumed by all + * tasks in the Isolation Group. + * + * @return User time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuUserUsage(); + + /** + * Returns the aggregate system time, in nanoseconds, consumed by + * all tasks in the Isolation Group. + * + * @return System time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuSystemUsage(); + + /***************************************************************** + * CPU Scheduling Metrics + ****************************************************************/ + + /** + * Returns the length of the scheduling period, in + * microseconds, for processes within the Isolation Group. + * + * @return time in microseconds or 0L if metric is not available. + * + */ + public long getCpuPeriod(); + + /** + * Returns the total available run-time allowed, in microseconds, + * during each scheduling period for all tasks in the Isolation + * Group. + * + * @return time in microseconds or -1 if the quota is unlimited. + * + */ + public long getCpuQuota(); + + + /** + * Returns the relative weighting of processes with the Isolation + * Group used for prioritizing the scheduling of processes across + * all Isolation Groups running on a host. + * + * @implNote + * Popular container orchestration systems have standardized shares + * to be multiples of 1024, where 1024 is interpreted as 1 CPU share + * of execution. Users can distribute CPU resources to multiple + * Isolation Groups by specifying the CPU share weighting needed by + * each process. To request 2 CPUS worth of execution time, CPU shares + * would be set to 2048. + * + * @return shares value or -1 if no share set. + * + */ + public long getCpuShares(); + + /** + * Returns the number of time-slice periods that have elapsed if + * a CPU quota has been setup for the Isolation Group; otherwise + * returns 0. + * + * @return count of elapsed periods or 0 if the quota is unlimited. + * + */ + public long getCpuNumPeriods(); + + /** + * Returns the number of time-slice periods that the group has + * been throttled or limited due to the group exceeding its quota + * if a CPU quota has been setup for the Isolation Group. + * + * @return count of throttled periods or 0 if the quota is unlimited. + * + */ + public long getCpuNumThrottled(); + + /** + * Returns the total time duration, in nanoseconds, that the + * group has been throttled or limited due to the group exceeding + * its quota if a CPU quota has been setup for the Isolation Group. + * + * @return Throttled time in nanoseconds or 0 if the quota is unlimited. + * + */ + public long getCpuThrottledTime(); + + + /** + * Returns the number of effective processors that this Isolation + * group has available to it. This effective processor count is + * computed based on the number of dedicated CPUs, CPU shares and + * CPU quotas in effect for this isolation group. + * + * This method returns the same value as + * {@link java.lang.Runtime#availableProcessors()}. + * + * @return The number of effective CPUs. + * + */ + public long getEffectiveCpuCount(); + + /***************************************************************** + * CPU Sets + ****************************************************************/ + + /** + * Returns the CPUS that are available for execution of processes + * in the current Isolation Group. The size of the array is equal + * to the total number of CPUs and the elements in the array are the + * physical CPU numbers that are available. Some of the CPUs returned + * may be offline. To get the current online CPUs, use + * {@link getEffectiveCpuSetCpus()}. + * + * @return An array of available CPUs or a zero length array + * if the metric is not available. + * + */ + public int[] getCpuSetCpus(); + + /** + * Returns the CPUS that are available and online for execution of + * processes within the current Isolation Group. The size of the + * array is equal to the total number of CPUs and the elements in + * the array are the physical CPU numbers. + * + * @return An array of available and online CPUs or a zero length + * array if the metric is not available. + * + */ + public int[] getEffectiveCpuSetCpus(); + + /** + * Returns the memory nodes that are available for use by processes + * in the current Isolation Group. The size of the array is equal + * to the total number of nodes and the elements in the array are the + * physical node numbers that are available. Some of the nodes returned + * may be offline. To get the current online memory nodes, use + * {@link getEffectiveCpuSetMems()}. + * + * @return An array of available memory nodes or a zero length array + * if the metric is not available. + * + */ + public int[] getCpuSetMems(); + + /** + * Returns the memory nodes that are available and online for use by + * processes within the current Isolation Group. The size of the + * array is equal to the total number of nodes and the elements in + * the array are the physical node numbers. + * + * @return An array of available and online nodes or a zero length + * array if the metric is not available. + * + */ + public int[] getEffectiveCpuSetMems(); + + /** + * Returns the (attempts per second * 1000), if enabled, that the + * operating system tries to satisfy a memory request for any + * process in the current Isolation Group when no free memory is + * readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to + * to determine if this support is enabled. + * + * @return Memory pressure or 0 if not enabled or metric is not + * available. + * + */ + public double getCpuSetMemoryPressure(); + + /** + * Returns the state of the memory pressure detection support. + * + * @return true if the support is available and enabled, otherwise false. + * + */ + public boolean isCpuSetMemoryPressureEnabled(); + + /***************************************************************** + * Memory Subsystem + ****************************************************************/ + + /** + * Returns the number of times that user memory requests in the + * Isolation Group have exceeded the memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getMemoryFailCount(); + + /** + * Returns the maximum amount of physical memory, in bytes, that + * can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getMemoryLimit(); + + /** + * Returns the largest amount of physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getMemoryMaxUsage(); + + /** + * Returns the amount of physical memory, in bytes, that is currently + * allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getMemoryUsage(); + + /** + * Returns the number of times that kernel memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getKernelMemoryFailCount(); + + /** + * Returns the maximum amount of kernel physical memory, in bytes, that + * can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getKernelMemoryLimit(); + + /** + * Returns the largest amount of kernel physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getKernelMemoryMaxUsage(); + + /** + * Returns the amount of kernel physical memory, in bytes, that + * is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getKernelMemoryUsage(); + + /** + * Returns the number of times that networking memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getTcpMemoryFailCount(); + + /** + * Returns the maximum amount of networking physical memory, in bytes, + * that can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getTcpMemoryLimit(); + + /** + * Returns the largest amount of networking physical memory, in bytes, + * that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getTcpMemoryMaxUsage(); + + /** + * Returns the amount of networking physical memory, in bytes, that + * is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getTcpMemoryUsage(); + + /** + * Returns the number of times that user memory requests in the + * Isolation Group have exceeded the memory + swap limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getMemoryAndSwapFailCount(); + + /** + * Returns the maximum amount of physical memory and swap space, + * in bytes, that can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getMemoryAndSwapLimit(); + + /** + * Returns the largest amount of physical memory and swap space, + * in bytes, that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getMemoryAndSwapMaxUsage(); + + /** + * Returns the amount of physical memory and swap space, in bytes, + * that is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getMemoryAndSwapUsage(); + + /** + * Returns the state of the Operating System Out of Memory termination + * policy. + * + * @return Returns true if operating system will terminate processes + * in the Isolation Group that exceed the amount of available + * memory, otherwise false. Flase will be returned if this + * capability is not available on the current operating system. + * + */ + public boolean isMemoryOOMKillEnabled(); + + /** + * Returns the hint to the operating system that allows groups + * to specify the minimum amount of physical memory that they need to + * achieve reasonable performance in low memory systems. This allows + * host systems to provide greater sharing of memory. + * + * @return The minimum amount of physical memory, in bytes, that the + * operating system will try to maintain under low memory + * conditions. If this metric is not available, 0 will be + * returned. + * + */ + public long getMemorySoftLimit(); + + /***************************************************************** + * BlKIO Subsystem + ****************************************************************/ + + /** + * Returns the number of block I/O requests to the disk that have been + * issued by the Isolation Group. + * + * @return The count of requests or 0 if this metric is not available. + * + */ + public long getBlkIOServiceCount(); + + /** + * Returns the number of block I/O bytes that have been transferred + * to/from the disk by the Isolation Group. + * + * @return The number of bytes transferred or 0 if this metric is not available. + * + */ + public long getBlkIOServiced(); +} diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 256471aed28..9837727d472 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -152,8 +152,7 @@ module java.base { jdk.jartool, jdk.jfr, jdk.jlink, - jdk.scripting.nashorn, - jdk.internal.vm.ci; + jdk.scripting.nashorn; exports jdk.internal.org.objectweb.asm.tree to jdk.jfr, jdk.jlink; @@ -182,7 +181,6 @@ module java.base { jdk.charsets, jdk.compiler, java.net.http, - jdk.jdeps, jdk.jfr, jdk.jlink, jdk.jshell, @@ -212,11 +210,11 @@ module java.base { jdk.scripting.nashorn, jdk.unsupported; exports jdk.internal.vm to - jdk.management.agent, - jdk.internal.jvmstat; + jdk.internal.jvmstat, + jdk.management.agent; exports jdk.internal.vm.annotation to - jdk.unsupported, - jdk.internal.vm.ci; + jdk.internal.vm.ci, + jdk.unsupported; exports jdk.internal.util.jar to jdk.jartool; exports jdk.internal.util.xml to @@ -260,8 +258,7 @@ module java.base { java.management, java.management.rmi, java.rmi, - java.sql.rowset, - java.xml; + java.sql.rowset; exports sun.security.action to java.desktop, java.security.jgss; @@ -312,12 +309,9 @@ module java.base { exports sun.security.x509 to jdk.crypto.ec, jdk.crypto.cryptoki, - jdk.jartool, - jdk.security.auth; + jdk.jartool; exports sun.security.validator to jdk.jartool; - exports sun.text.resources to - jdk.localedata; exports sun.util.cldr to jdk.jlink; exports sun.util.locale.provider to diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index 4b10ca85b72..d1c4e5590fb 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -91,6 +91,9 @@ import java.util.stream.Stream; import jdk.internal.misc.VM; import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.Modules; +import jdk.internal.platform.Container; +import jdk.internal.platform.Metrics; + public final class LauncherHelper { @@ -151,6 +154,7 @@ public final class LauncherHelper { * this code determine this value, using a suitable method or omit the * line entirely. */ + @SuppressWarnings("fallthrough") static void showSettings(boolean printToStderr, String optionFlag, long initialHeapSize, long maxHeapSize, long stackSize) { @@ -169,10 +173,18 @@ public final class LauncherHelper { case "locale": printLocale(); break; + case "system": + if (System.getProperty("os.name").contains("Linux")) { + printSystemMetrics(); + break; + } default: printVmSettings(initialHeapSize, maxHeapSize, stackSize); printProperties(); printLocale(); + if (System.getProperty("os.name").contains("Linux")) { + printSystemMetrics(); + } break; } } @@ -307,6 +319,101 @@ public final class LauncherHelper { } } + public static void printSystemMetrics() { + Metrics c = Container.metrics(); + + ostream.println("Operating System Metrics:"); + + if (c == null) { + ostream.println(INDENT + "No metrics available for this platform"); + return; + } + + ostream.println(INDENT + "Provider: " + c.getProvider()); + ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount()); + ostream.println(INDENT + "CPU Period: " + c.getCpuPeriod() + + (c.getCpuPeriod() == -1 ? "" : "us")); + ostream.println(INDENT + "CPU Quota: " + c.getCpuQuota() + + (c.getCpuQuota() == -1 ? "" : "us")); + ostream.println(INDENT + "CPU Shares: " + c.getCpuShares()); + + int cpus[] = c.getCpuSetCpus(); + ostream.println(INDENT + "List of Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + + cpus = c.getEffectiveCpuSetCpus(); + ostream.println(INDENT + "List of Effective Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + + int mems[] = c.getCpuSetMems(); + ostream.println(INDENT + "List of Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + + mems = c.getEffectiveCpuSetMems(); + ostream.println(INDENT + "List of Available Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + + ostream.println(INDENT + "CPUSet Memory Pressure Enabled: " + + c.isCpuSetMemoryPressureEnabled()); + + long limit = c.getMemoryLimit(); + ostream.println(INDENT + "Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getMemorySoftLimit(); + ostream.println(INDENT + "Memory Soft Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getMemoryAndSwapLimit(); + ostream.println(INDENT + "Memory & Swap Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getKernelMemoryLimit(); + ostream.println(INDENT + "Kernel Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getTcpMemoryLimit(); + ostream.println(INDENT + "TCP Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + ostream.println(INDENT + "Out Of Memory Killer Enabled: " + + c.isMemoryOOMKillEnabled()); + + ostream.println(""); + } + private enum SizePrefix { KILO(1024, "K"), diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 63e21860a09..41dd8da5602 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -154,7 +154,11 @@ java.launcher.X.usage=\n\ \ show all locale related settings and continue\n\ \ -XshowSettings:properties\n\ \ show all property settings and continue\n\ -\ -XshowSettings:vm show all vm related settings and continue\n\ +\ -XshowSettings:vm\n\ +\ show all vm related settings and continue\n\ +\ -XshowSettings:system\n\ +\ (Linux Only) show host system or container\n\ +\ configuration and continue\n\ \ -Xss set java thread stack size\n\ \ -Xverify sets the mode of the bytecode verifier\n\ \ --add-reads =(,)*\n\ diff --git a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java index 20f911098cd..adc5326f576 100644 --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java @@ -715,19 +715,23 @@ public class AnnotationParser { ConstantPool constPool, Class container) { Object[] result = new Class[length]; - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == 'c') { - result[i] = parseClassValue(buf, constPool, container); + Object value = parseClassValue(buf, constPool, container); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } private static Object parseEnumArray(int length, Class> enumType, @@ -735,19 +739,23 @@ public class AnnotationParser { ConstantPool constPool, Class container) { Object[] result = (Object[]) Array.newInstance(enumType, length); - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == 'e') { - result[i] = parseEnumValue(enumType, buf, constPool, container); + Object value = parseEnumValue(enumType, buf, constPool, container); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } private static Object parseAnnotationArray(int length, @@ -756,19 +764,23 @@ public class AnnotationParser { ConstantPool constPool, Class container) { Object[] result = (Object[]) Array.newInstance(annotationType, length); - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == '@') { - result[i] = parseAnnotation(buf, constPool, container, true); + Object value = parseAnnotation(buf, constPool, container, true); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } /** diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java index 160f413b9c6..3802eb14571 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java @@ -178,13 +178,27 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl { // last resort String id = names[INDEX_TZID].toUpperCase(Locale.ROOT); - if (!id.startsWith("ETC/GMT") && - !id.startsWith("GMT") && - !id.startsWith("UT")) { + if (!id.startsWith("UT")) { names[index] = toGMTFormat(names[INDEX_TZID], index == INDEX_DST_LONG || index == INDEX_DST_SHORT, index % 2 != 0, locale); + // aliases of "GMT" timezone. + if ((exists(names, INDEX_STD_LONG)) && (id.startsWith("Etc/") + || id.startsWith("GMT") || id.startsWith("Greenwich"))) { + switch (id) { + case "Etc/GMT": + case "Etc/GMT-0": + case "Etc/GMT+0": + case "Etc/GMT0": + case "GMT+0": + case "GMT-0": + case "GMT0": + case "Greenwich": + names[INDEX_DST_LONG] = names[INDEX_GEN_LONG] = names[INDEX_STD_LONG]; + break; + } + } } } diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java index 3e4a0dab75a..3069202077f 100644 --- a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java @@ -522,6 +522,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Porto_Acre", ACT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, + {"America/Punta_Arenas", new String[] {"Punta Arenas Standard Time", "GMT-03:00", + "Punta Arenas Standard Time", "GMT-03:00", + "Punta Arenas Time", "GMT-03:00"}}, {"America/Rainy_River", CST}, {"America/Rankin_Inlet", CST}, {"America/Recife", BRT}, @@ -630,6 +633,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Asia/Dushanbe", new String[] {"Tajikistan Time", "TJT", "Tajikistan Summer Time", "TJST", "Tajikistan Time", "TJT"}}, + {"Asia/Famagusta", EET}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, {"Asia/Hebron", EET}, @@ -801,6 +805,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Etc/Zulu", UTC}, {"Europe/Amsterdam", CET}, {"Europe/Andorra", CET}, + {"Europe/Astrakhan", new String[] {"Astrakhan Standard Time", "GMT+04:00", + "Astrakhan Standard Time", "GMT+04:00", + "Astrakhan Time", "GMT+04:00"}}, {"Europe/Athens", EET}, {"Europe/Belfast", GMTBST}, {"Europe/Belgrade", CET}, @@ -841,6 +848,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { "Samara Time", "SAMT"}}, {"Europe/San_Marino", CET}, {"Europe/Sarajevo", CET}, + {"Europe/Saratov", new String[] {"Saratov Standard Time", "GMT+04:00", + "Saratov Standard Time", "GMT+04:00", + "Saratov Time", "GMT+04:00"}}, {"Europe/Simferopol", MSK}, {"Europe/Skopje", CET}, {"Europe/Sofia", EET}, @@ -848,6 +858,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Europe/Tallinn", EET}, {"Europe/Tirane", CET}, {"Europe/Tiraspol", EET}, + {"Europe/Ulyanovsk", new String[] {"Ulyanovsk Standard Time", "GMT+04:00", + "Ulyanovsk Standard Time", "GMT+04:00", + "Ulyanovsk Time", "GMT+04:00"}}, {"Europe/Uzhgorod", EET}, {"Europe/Vaduz", CET}, {"Europe/Vatican", CET}, diff --git a/src/java.base/share/lib/security/cacerts b/src/java.base/share/lib/security/cacerts index 2796af58140..350636c26f1 100644 Binary files a/src/java.base/share/lib/security/cacerts and b/src/java.base/share/lib/security/cacerts differ diff --git a/src/java.base/unix/native/libjava/FileOutputStream_md.c b/src/java.base/unix/native/libjava/FileOutputStream_md.c index b14e8847f81..407cb9ad23b 100644 --- a/src/java.base/unix/native/libjava/FileOutputStream_md.c +++ b/src/java.base/unix/native/libjava/FileOutputStream_md.c @@ -70,7 +70,3 @@ Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, writeBytes(env, this, bytes, off, len, append, fos_fd); } -void JNICALL -Java_java_io_FileOutputStream_close0(JNIEnv *env, jobject this) { - fileClose(env, this, fos_fd); -} 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 8a4899740f4..15f38f849c0 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -121,16 +121,6 @@ fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags) } END_PLATFORM_STRING(env, ps); } -void -fileClose(JNIEnv *env, jobject this, jfieldID fid) -{ - jobject fileDescriptor = (*env)->GetObjectField(env, (this), (fid)); - if (fileDescriptor == NULL) { - return; - } - fileDescriptorClose(env, fileDescriptor); -} - // Function to close the fd held by this FileDescriptor and set fd to -1. void fileDescriptorClose(JNIEnv *env, jobject this) diff --git a/src/java.base/unix/native/libjava/io_util_md.h b/src/java.base/unix/native/libjava/io_util_md.h index f4c8a4c767c..8c36cfdfde4 100644 --- a/src/java.base/unix/native/libjava/io_util_md.h +++ b/src/java.base/unix/native/libjava/io_util_md.h @@ -99,10 +99,6 @@ FD handleOpen(const char *path, int oflag, int mode); } while((_result == -1) && (errno == EINTR)); \ } while(0) -/* - * IO helper function(s) - */ -void fileClose(JNIEnv *env, jobject this, jfieldID fid); void fileDescriptorClose(JNIEnv *env, jobject this); #ifdef MACOSX diff --git a/src/java.base/windows/native/libjava/FileOutputStream_md.c b/src/java.base/windows/native/libjava/FileOutputStream_md.c index 02ea15e89cb..452a3a41187 100644 --- a/src/java.base/windows/native/libjava/FileOutputStream_md.c +++ b/src/java.base/windows/native/libjava/FileOutputStream_md.c @@ -72,7 +72,3 @@ Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, writeBytes(env, this, bytes, off, len, append, fos_fd); } -JNIEXPORT void JNICALL -Java_java_io_FileOutputStream_close0(JNIEnv *env, jobject this) { - handleClose(env, this, fos_fd); -} diff --git a/src/java.base/windows/native/libjava/io_util_md.c b/src/java.base/windows/native/libjava/io_util_md.c index b7ef414f0e2..c077e2e6742 100644 --- a/src/java.base/windows/native/libjava/io_util_md.c +++ b/src/java.base/windows/native/libjava/io_util_md.c @@ -535,16 +535,6 @@ jint handleAppend(FD fd, const void *buf, jint len) { return writeInternal(fd, buf, len, JNI_TRUE); } -void -handleClose(JNIEnv *env, jobject this, jfieldID fid) -{ - jobject fileDescriptor = (*env)->GetObjectField(env, (this), (fid)); - if (fileDescriptor == NULL) { - return; - } - fileDescriptorClose(env, fileDescriptor); -} - // Function to close the fd held by this FileDescriptor and set fd to -1. void fileDescriptorClose(JNIEnv *env, jobject this) diff --git a/src/java.base/windows/native/libjava/io_util_md.h b/src/java.base/windows/native/libjava/io_util_md.h index 9d95636353a..66f9ab5b214 100644 --- a/src/java.base/windows/native/libjava/io_util_md.h +++ b/src/java.base/windows/native/libjava/io_util_md.h @@ -48,7 +48,6 @@ jlong handleGetLength(FD fd); JNIEXPORT jint handleRead(FD fd, void *buf, jint len); jint handleWrite(FD fd, const void *buf, jint len); jint handleAppend(FD fd, const void *buf, jint len); -void handleClose(JNIEnv *env, jobject this, jfieldID fid); void fileDescriptorClose(JNIEnv *env, jobject this); JNIEXPORT jlong JNICALL handleLseek(FD fd, jlong offset, jint whence); diff --git a/src/java.compiler/share/classes/javax/annotation/processing/Messager.java b/src/java.compiler/share/classes/javax/annotation/processing/Messager.java index daed8db7ce4..f79e64ef584 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/Messager.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/Messager.java @@ -25,7 +25,6 @@ package javax.annotation.processing; -import javax.annotation.*; import javax.tools.Diagnostic; import javax.lang.model.element.*; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 9f643110332..33b822bf462 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -181,7 +181,8 @@ public enum Source { DIAMOND_WITH_ANONYMOUS_CLASS_CREATION(JDK9, Fragments.FeatureDiamondAndAnonClass, DiagKind.NORMAL), UNDERSCORE_IDENTIFIER(MIN, JDK8), PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL), - LOCAL_VARIABLE_TYPE_INFERENCE(JDK10); + LOCAL_VARIABLE_TYPE_INFERENCE(JDK10), + IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8); enum DiagKind { NORMAL, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 665b8cd2d0f..90cb9d1894f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -3650,17 +3650,8 @@ public class Check { OUTER: for (JCImport imp : toplevel.getImports()) { if (!imp.staticImport && TreeInfo.name(imp.qualid) == names.asterisk) { TypeSymbol tsym = ((JCFieldAccess)imp.qualid).selected.type.tsym; - if (toplevel.modle.visiblePackages != null) { - //TODO - unclear: selects like javax.* will get resolved from the current module - //(as javax is not an exported package from any module). And as javax in the current - //module typically does not contain any classes or subpackages, we need to go through - //the visible packages to find a sub-package: - for (PackageSymbol known : toplevel.modle.visiblePackages.values()) { - if (Convert.packagePart(known.fullname) == tsym.flatName()) - continue OUTER; - } - } - if (tsym.kind == PCK && tsym.members().isEmpty() && !tsym.exists()) { + if (tsym.kind == PCK && tsym.members().isEmpty() && + !(Feature.IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES.allowedInSource(source) && tsym.exists())) { log.error(DiagnosticFlag.RESOLVE_ERROR, imp.pos, Errors.DoesntExist(tsym)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java index e0626c973d6..05558e5d64d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java @@ -31,6 +31,7 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Type.StructuralTypeMapping; import com.sun.tools.javac.code.Types.TypeMapping; import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; +import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; import com.sun.tools.javac.comp.Resolve.ResolveError; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.tree.*; @@ -164,11 +165,18 @@ public class DeferredAttr extends JCTree.Visitor { JCMemberReference result = new JCMemberReference(t.mode, t.name, expr, typeargs) { @Override public void setOverloadKind(OverloadKind overloadKind) { - super.setOverloadKind(overloadKind); - if (t.getOverloadKind() == null) { + OverloadKind previous = t.getOverloadKind(); + if (previous == null) { t.setOverloadKind(overloadKind); + } else { + Assert.check(previous == overloadKind); } } + + @Override + public OverloadKind getOverloadKind() { + return t.getOverloadKind(); + } }; result.pos = t.pos; return result; @@ -656,28 +664,57 @@ public class DeferredAttr extends JCTree.Visitor { } /** - * Pick the deferred node to be unstuck. The chosen node is the first strongly connected - * component containing exactly one node found in the dependency graph induced by deferred nodes. - * If no such component is found, the first deferred node is returned. + * Pick the deferred node to be unstuck. First, deferred nodes are organized into a graph + * (see {@code DeferredAttrContext.buildStuckGraph()}, where a node N1 depends on another node N2 + * if its input variable depends (as per the inference graph) on the output variables of N2 + * (see {@code DeferredAttrContext.canInfluence()}. + * + * Then, the chosen deferred node is the first strongly connected component containing exactly + * one node found in such a graph. If no such component is found, the first deferred node is chosen. */ DeferredAttrNode pickDeferredNode() { + List stuckGraph = buildStuckGraph(); + //compute tarjan on the stuck graph + List csn = GraphUtils.tarjan(stuckGraph).get(0); + return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0); + } + + List buildStuckGraph() { + //first, build inference graph + infer.doIncorporation(inferenceContext, warn); + InferenceGraph graph = infer.new GraphSolver(inferenceContext, types.noWarnings) + .new InferenceGraph(); + //then, build stuck graph List nodes = deferredAttrNodes.stream() .map(StuckNode::new) .collect(List.collector()); //init stuck expression graph; a deferred node A depends on a deferred node B iff - //the intersection between A's input variable and B's output variable is non-empty. + //B's output variables can influence A's input variables. for (StuckNode sn1 : nodes) { - for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) { - for (StuckNode sn2 : nodes) { - if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) { - sn1.deps.add(sn2); - } + for (StuckNode sn2 : nodes) { + if (sn1 != sn2 && canInfluence(graph, sn2, sn1)) { + sn1.deps.add(sn2); } } } - //compute tarjan on the stuck graph - List csn = GraphUtils.tarjan(nodes).get(0); - return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0); + return nodes; + } + + boolean canInfluence(InferenceGraph graph, StuckNode sn1, StuckNode sn2) { + Set outputVars = sn1.data.deferredStuckPolicy.depVars(); + for (Type inputVar : sn2.data.deferredStuckPolicy.stuckVars()) { + InferenceGraph.Node inputNode = graph.findNode(inputVar); + //already solved stuck vars do not appear in the graph + if (inputNode != null) { + Set inputClosure = inputNode.closure(); + if (outputVars.stream() + .map(graph::findNode) + .anyMatch(inputClosure::contains)) { + return true; + } + } + } + return false; } class StuckNode extends GraphUtils.TarjanNode { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java index fd3ff7b2f3a..7edf8823f89 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java @@ -61,6 +61,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -1706,7 +1707,7 @@ public class Infer { Node(Type ivar) { super(ListBuffer.of(ivar)); - this.deps = new HashSet<>(); + this.deps = new LinkedHashSet<>(); } @Override @@ -1750,6 +1751,24 @@ public class Infer { return deps.remove(n); } + /** + * Compute closure of a give node, by recursively walking + * through all its dependencies. + */ + protected Set closure() { + Set closure = new HashSet<>(); + closureInternal(closure); + return closure; + } + + private void closureInternal(Set closure) { + if (closure.add(this)) { + for (Node n : deps) { + n.closureInternal(closure); + } + } + } + /** * Is this node a leaf? This means either the node has no dependencies, * or it just has self-dependencies. @@ -1777,7 +1796,7 @@ public class Infer { addDependencies(n.deps); } //update deps - Set deps2 = new HashSet<>(); + Set deps2 = new LinkedHashSet<>(); for (Node d : deps) { if (data.contains(d.data.first())) { deps2.add(this); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index c4b5f41e712..ff5733c3e36 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -69,8 +69,6 @@ import java.util.stream.Stream; import javax.lang.model.element.ElementVisitor; -import com.sun.tools.javac.comp.Infer.InferenceException; - import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Flags.STATIC; @@ -818,9 +816,29 @@ public class Resolve { String key = inferDiag ? diag.inferKey : diag.basicKey; throw inferDiag ? infer.error(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)) : - new InapplicableMethodException(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); + methodCheckFailure.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); } + /** + * To eliminate the overhead associated with allocating an exception object in such an + * hot execution path, we use flyweight pattern - and share the same exception instance + * across multiple method check failures. + */ + class SharedInapplicableMethodException extends InapplicableMethodException { + private static final long serialVersionUID = 0; + + SharedInapplicableMethodException() { + super(null); + } + + SharedInapplicableMethodException setMessage(JCDiagnostic details) { + this.diagnostic = details; + return this; + } + } + + SharedInapplicableMethodException methodCheckFailure = new SharedInapplicableMethodException(); + public MethodCheck mostSpecificCheck(List actuals) { return nilMethodCheck; } @@ -2012,7 +2030,7 @@ public class Resolve { } } return null; - }, sym -> sym.kind == Kind.TYP, false, typeNotFound); + }, sym -> sym.kind == Kind.TYP, typeNotFound); } }; @@ -2049,18 +2067,11 @@ public class Resolve { PackageSymbol pack = syms.lookupPackage(env.toplevel.modle, name); if (allowModules && isImportOnDemand(env, name)) { - pack.complete(); - if (!pack.exists()) { - Name nameAndDot = name.append('.', names.empty); - boolean prefixOfKnown = - env.toplevel.modle.visiblePackages.values() - .stream() - .anyMatch(p -> p.fullname.startsWith(nameAndDot)); - + if (pack.members().isEmpty()) { return lookupInvisibleSymbol(env, name, syms::getPackagesForName, syms::enterPackage, sym -> { sym.complete(); - return sym.exists(); - }, prefixOfKnown, pack); + return !sym.members().isEmpty(); + }, pack); } } @@ -2087,7 +2098,6 @@ public class Resolve { Function> get, BiFunction load, Predicate validate, - boolean suppressError, Symbol defaultResult) { //even if a class/package cannot be found in the current module and among packages in modules //it depends on that are exported for any or this module, the class/package may exist internally @@ -2097,7 +2107,7 @@ public class Resolve { for (S sym : candidates) { if (validate.test(sym)) - return createInvisibleSymbolError(env, suppressError, sym); + return createInvisibleSymbolError(env, sym); } Set recoverableModules = new HashSet<>(syms.getAllModules()); @@ -2117,7 +2127,7 @@ public class Resolve { S sym = load.apply(ms, name); if (sym != null && validate.test(sym)) { - return createInvisibleSymbolError(env, suppressError, sym); + return createInvisibleSymbolError(env, sym); } } } @@ -2126,11 +2136,11 @@ public class Resolve { return defaultResult; } - private Symbol createInvisibleSymbolError(Env env, boolean suppressError, Symbol sym) { + private Symbol createInvisibleSymbolError(Env env, Symbol sym) { if (symbolPackageVisible(env, sym)) { return new AccessError(env, null, sym); } else { - return new InvisibleSymbolError(env, suppressError, sym); + return new InvisibleSymbolError(env, false, sym); } } 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 ca7188fe7e9..5e1edf499f8 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 @@ -317,7 +317,7 @@ public enum Option { .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames() .spliterator(), false)) - .collect(Collectors.toCollection(TreeSet :: new)); + .collect(Collectors.toCollection(LinkedHashSet :: new)); StringBuilder targets = new StringBuilder(); String delim = ""; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 1015702b6da..26d2273ed5e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -3127,6 +3127,9 @@ public class JavacParser implements Parser { if (token.kind == LBRACKET) { log.error(token.pos, Errors.ArrayAndReceiver); } + if (pn.hasTag(Tag.SELECT) && ((JCFieldAccess)pn).name != names._this) { + log.error(token.pos, Errors.WrongReceiver); + } } return toP(F.at(pos).ReceiverVarDef(mods, pn, type)); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java index fbb7cc57d05..22fa43c991d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -39,6 +39,7 @@ import java.nio.file.ProviderNotFoundException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; @@ -89,9 +90,24 @@ public class JDKPlatformProvider implements PlatformProvider { private static final String[] symbolFileLocation = { "lib", "ct.sym" }; private static final Set SUPPORTED_JAVA_PLATFORM_VERSIONS; + public static final Comparator NUMERICAL_COMPARATOR = (s1, s2) -> { + int i1; + try { + i1 = Integer.parseInt(s1); + } catch (NumberFormatException ex) { + i1 = Integer.MAX_VALUE; + } + int i2; + try { + i2 = Integer.parseInt(s2); + } catch (NumberFormatException ex) { + i2 = Integer.MAX_VALUE; + } + return i1 != i2 ? i1 - i2 : s1.compareTo(s2); + }; static { - SUPPORTED_JAVA_PLATFORM_VERSIONS = new TreeSet<>(); + SUPPORTED_JAVA_PLATFORM_VERSIONS = new TreeSet<>(NUMERICAL_COMPARATOR); Path ctSymFile = findCtSym(); if (Files.exists(ctSymFile)) { try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, null); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 480bd272a9f..90fb14fe5b5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -677,6 +677,9 @@ compiler.err.varargs.must.be.last =\ compiler.err.array.and.receiver =\ legacy array notation not allowed on receiver parameter +compiler.err.wrong.receiver =\ + wrong receiver parameter name + compiler.err.variable.not.allowed=\ variable declaration not allowed here diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/DataLayout.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/DataLayout.java index 4c992f176a8..f297cdb9c98 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/DataLayout.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/DataLayout.java @@ -47,19 +47,14 @@ public class DataLayout { public static final int parametersTypeDataTag = 12; public static final int speculativeTrapDataTag = 13; - // The _struct._flags word is formatted as [trapState:4 | flags:4]. - // The trap state breaks down further as [recompile:1 | reason:3]. + // The trap state breaks down as [recompile:1 | reason:31]. // This further breakdown is defined in deoptimization.cpp. // See Deoptimization.trapStateReason for an assert that // trapBits is big enough to hold reasons < reasonRecordedLimit. // // The trapState is collected only if ProfileTraps is true. - public static final int trapBits = 1+3; // 3: enough to distinguish [0..reasonRecordedLimit]. - public static final int trapShift = 8 - trapBits; + public static final int trapBits = 1+31; // 31: enough to distinguish [0..reasonRecordedLimit]. public static final int trapMask = Bits.rightNBits(trapBits); - public static final int trapMaskInPlace = (trapMask << trapShift); - public static final int flagLimit = trapShift; - public static final int flagMask = Bits.rightNBits(flagLimit); public static final int firstFlag = 0; private Address data; @@ -97,16 +92,17 @@ public class DataLayout { // Every data layout begins with a header. This header // contains a tag, which is used to indicate the size/layout - // of the data, 4 bits of flags, which can be used in any way, - // 4 bits of trap history (none/one reason/many reasons), + // of the data, 8 bits of flags, which can be used in any way, + // 32 bits of trap history (none/one reason/many reasons), // and a bci, which is used to tie this piece of data to a // specific bci in the bytecodes. // union { - // intptrT _bits; + // u8 _bits; // struct { // u1 _tag; // u1 _flags; // u2 _bci; + // u4 _traps; // } _struct; // } _header; @@ -119,10 +115,10 @@ public class DataLayout { // Size computation static int headerSizeInBytes() { - return MethodData.cellSize; + return MethodData.cellSize * headerSizeInCells(); } static int headerSizeInCells() { - return 1; + return VM.getVM().isLP64() ? 1 : 2; } static public int computeSizeInBytes(int cellCount) { @@ -146,7 +142,7 @@ public class DataLayout { // simplifying assumption that all N occurrences can be blamed // on that BCI. int trapState() { - return (flags() >> trapShift) & trapMask; + return data.getJIntAt(offset+4); } int flags() { 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 a8da801532d..4ac6ce4c8ad 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, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -24,6 +24,7 @@ package sun.jvm.hotspot.runtime; +import java.nio.charset.StandardCharsets; import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.oops.*; @@ -362,11 +363,8 @@ public class PerfDataEntry extends VMObject { str = new String(charArrayValue()); } 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(); - } + str = CStringUtilities.getString(addr.addOffsetTo(dataOffset()), + StandardCharsets.US_ASCII); } else if (dataType == BasicType.getTShort()) { short[] res = shortArrayValue(); StringBuffer buf = new StringBuffer(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java index 34fe1755246..68c631a4e11 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CStringUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -25,6 +25,7 @@ package sun.jvm.hotspot.utilities; import java.io.*; +import java.nio.charset.Charset; import java.util.*; import sun.jvm.hotspot.debugger.*; @@ -45,11 +46,15 @@ public class CStringUtilities { private static String encoding = System.getProperty("file.encoding", "US-ASCII"); + public static String getString(Address addr) { + return getString(addr, Charset.forName(encoding)); + } + /** Fetch a null-terminated ASCII string from the remote process. Returns null if the argument is null, otherwise returns a non-null string (for example, returns an empty string if the first character fetched is the null terminator). */ - public static String getString(Address addr) { + public static String getString(Address addr, Charset charset) { if (addr == null) { return null; } @@ -73,10 +78,6 @@ public class CStringUtilities { // FIXME: When we switch to use JDK 6 to build SA, // we can change the following to just return: // return new String(bytes, Charset.defaultCharset()); - try { - return new String(bytes, encoding); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Error converting bytes to String using " + encoding + " encoding", e); - } + return new String(bytes, charset); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 600b0e0e257..508cb4e01cb 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -207,7 +207,8 @@ public class HtmlDoclet extends AbstractDoclet { private void copyJqueryFiles() throws DocletException { List files = Arrays.asList( - "jquery-1.12.4.js", + "jquery-3.3.1.js", + "jquery-migrate-3.0.1.js", "jquery-ui.js", "jquery-ui.css", "jquery-ui.min.js", diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java index a9d76cda97d..b988ec9f379 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java @@ -344,7 +344,8 @@ public class Head { tree.addContent(new RawHtml("")); - addJQueryFile(tree, DocPaths.JQUERY_JS_1_12); + addJQueryFile(tree, DocPaths.JQUERY_JS_3_3); + addJQueryFile(tree, DocPaths.JQUERY_MIGRATE); addJQueryFile(tree, DocPaths.JQUERY_JS); } for (Script script : scripts) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js index 7fc60fca78b..9b5206bcc60 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js @@ -1,20 +1,22 @@ /*! - * jQuery JavaScript Library v1.12.4 - * http://jquery.com/ + * jQuery JavaScript Library v3.3.1 + * https://jquery.com/ * * Includes Sizzle.js - * http://sizzlejs.com/ + * https://sizzlejs.com/ * - * Copyright jQuery Foundation and other contributors + * Copyright JS Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://jquery.org/license * - * Date: 2016-05-20T17:17Z + * Date: 2018-01-20T17:24Z */ +( function( global, factory ) { -(function( global, factory ) { + "use strict"; if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` // is present, execute the factory and get jQuery. // For environments that do not have a `window` with a `document` @@ -35,24 +37,27 @@ } // Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { -// Support: Firefox 18+ -// Can't be in strict mode, several libs including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -//"use strict"; -var deletedIds = []; +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; var document = window.document; -var slice = deletedIds.slice; +var getProto = Object.getPrototypeOf; -var concat = deletedIds.concat; +var slice = arr.slice; -var push = deletedIds.push; +var concat = arr.concat; -var indexOf = deletedIds.indexOf; +var push = arr.push; + +var indexOf = arr.indexOf; var class2type = {}; @@ -60,12 +65,71 @@ var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + var support = {}; +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + noModule: true + }; + + function DOMEval( code, doc, node ) { + doc = doc || document; + + var i, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + if ( node[ i ] ) { + script[ i ] = node[ i ]; + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + var - version = "1.12.4", + version = "3.3.1", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -75,18 +139,9 @@ var return new jQuery.fn.init( selector, context ); }, - // Support: Android<4.1, IE<9 + // Support: Android <=4.0 only // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; jQuery.fn = jQuery.prototype = { @@ -95,9 +150,6 @@ jQuery.fn = jQuery.prototype = { constructor: jQuery, - // Start with an empty selector - selector: "", - // The default length of a jQuery object is 0 length: 0, @@ -108,13 +160,14 @@ jQuery.fn = jQuery.prototype = { // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num != null ? - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } - // Return all the elements in a clean array - slice.call( this ); + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; }, // Take an array of elements and push it onto the stack @@ -126,7 +179,6 @@ jQuery.fn = jQuery.prototype = { // Add the old object onto the stack (as a reference) ret.prevObject = this; - ret.context = this.context; // Return the newly-formed element set return ret; @@ -168,12 +220,12 @@ jQuery.fn = jQuery.prototype = { // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, - sort: deletedIds.sort, - splice: deletedIds.splice + sort: arr.sort, + splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { - var src, copyIsArray, copy, name, options, clone, + var options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, length = arguments.length, @@ -183,17 +235,17 @@ jQuery.extend = jQuery.fn.extend = function() { if ( typeof target === "boolean" ) { deep = target; - // skip the boolean and the target + // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + if ( typeof target !== "object" && !isFunction( target ) ) { target = {}; } - // extend jQuery itself if only one argument is passed + // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; @@ -216,11 +268,11 @@ jQuery.extend = jQuery.fn.extend = function() { // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + ( copyIsArray = Array.isArray( copy ) ) ) ) { if ( copyIsArray ) { copyIsArray = false; - clone = src && jQuery.isArray( src ) ? src : []; + clone = src && Array.isArray( src ) ? src : []; } else { clone = src && jQuery.isPlainObject( src ) ? src : {}; @@ -255,110 +307,42 @@ jQuery.extend( { noop: function() {}, - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type( obj ) === "function"; - }, + isPlainObject: function( obj ) { + var proto, Ctor; - isArray: Array.isArray || function( obj ) { - return jQuery.type( obj ) === "array"; - }, + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } - isWindow: function( obj ) { - /* jshint eqeqeq: false */ - return obj != null && obj == obj.window; - }, + proto = getProto( obj ); - isNumeric: function( obj ) { + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - // adding 1 corrects loss of precision from parseFloat (#15100) - var realStringObj = obj && obj.toString(); - return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 var name; + for ( name in obj ) { return false; } return true; }, - isPlainObject: function( obj ) { - var key; - - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call( obj, "constructor" ) && - !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - } catch ( e ) { - - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Support: IE<9 - // Handle iteration over inherited properties before own properties. - if ( !support.ownFirst ) { - for ( key in obj ) { - return hasOwn.call( obj, key ); - } - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; - }, - - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && jQuery.trim( data ) ) { - - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); }, each: function( obj, callback ) { @@ -382,7 +366,7 @@ jQuery.extend( { return obj; }, - // Support: Android<4.1, IE<9 + // Support: Android <=4.0 only trim: function( text ) { return text == null ? "" : @@ -408,43 +392,18 @@ jQuery.extend( { }, inArray: function( elem, arr, i ) { - var len; - - if ( arr ) { - if ( indexOf ) { - return indexOf.call( arr, elem, i ); - } - - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } - - return -1; + return arr == null ? -1 : indexOf.call( arr, elem, i ); }, + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; - while ( j < len ) { - first[ i++ ] = second[ j++ ]; - } - - // Support: IE<9 - // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) - if ( len !== len ) { - while ( second[ j ] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; } first.length = i; @@ -506,53 +465,14 @@ jQuery.extend( { // A global GUID counter for objects guid: 1, - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var args, proxy, tmp; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: function() { - return +( new Date() ); - }, - // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support } ); -// JSHint would error on this code due to the Symbol not being defined in ES5. -// Defining this global in .jshintrc would create a danger of using the global -// unguarded in another place, it seems safer to just disable JSHint for these -// three lines. -/* jshint ignore: start */ if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; } -/* jshint ignore: end */ // Populate the class2type map jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), @@ -562,14 +482,14 @@ function( i, name ) { function isArrayLike( obj ) { - // Support: iOS 8.2 (not reproducible in simulator) + // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, - type = jQuery.type( obj ); + type = toType( obj ); - if ( type === "function" || jQuery.isWindow( obj ) ) { + if ( isFunction( obj ) || isWindow( obj ) ) { return false; } @@ -578,14 +498,14 @@ function isArrayLike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.2.1 - * http://sizzlejs.com/ + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-10-17 + * Date: 2016-08-08 */ (function( window ) { @@ -626,9 +546,6 @@ var i, return 0; }, - // General-purpose constants - MAX_NEGATIVE = 1 << 31, - // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], @@ -637,7 +554,7 @@ var i, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native - // http://jsperf.com/thor-indexof-vs-for/5 + // https://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; @@ -657,7 +574,7 @@ var i, whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + @@ -714,9 +631,9 @@ var i, rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, - rescape = /'|\\/g, - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; @@ -732,13 +649,39 @@ var i, String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); - }; + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); // Optimize for push.apply( _, NodeList ) try { @@ -770,7 +713,7 @@ try { } function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, nidselect, match, groups, newSelector, + var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document @@ -863,7 +806,7 @@ function Sizzle( selector, context, results, seed ) { // Capture the context ID, setting it first if necessary if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rescape, "\\$&" ); + nid = nid.replace( rcssescape, fcssescape ); } else { context.setAttribute( "id", (nid = expando) ); } @@ -871,9 +814,8 @@ function Sizzle( selector, context, results, seed ) { // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; - nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; while ( i-- ) { - groups[i] = nidselect + " " + toSelector( groups[i] ); + groups[i] = "#" + nid + " " + toSelector( groups[i] ); } newSelector = groups.join( "," ); @@ -934,22 +876,22 @@ function markFunction( fn ) { /** * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result + * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { - var div = document.createElement("div"); + var el = document.createElement("fieldset"); try { - return !!fn( div ); + return !!fn( el ); } catch (e) { return false; } finally { // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); + if ( el.parentNode ) { + el.parentNode.removeChild( el ); } // release memory in IE - div = null; + el = null; } } @@ -976,8 +918,7 @@ function addHandle( attrs, handler ) { function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); + a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes if ( diff ) { @@ -1018,6 +959,62 @@ function createButtonPseudo( type ) { }; } +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + /** * Returns a function to use in pseudos for positionals * @param {Function} fn @@ -1070,7 +1067,7 @@ isXML = Sizzle.isXML = function( elem ) { * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, parent, + var hasCompare, subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected @@ -1085,14 +1082,16 @@ setDocument = Sizzle.setDocument = function( node ) { // Support: IE 9-11, Edge // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - if ( (parent = document.defaultView) && parent.top !== parent ) { - // Support: IE 11 - if ( parent.addEventListener ) { - parent.addEventListener( "unload", unloadHandler, false ); + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); // Support: IE 9 - 10 only - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", unloadHandler ); + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); } } @@ -1102,18 +1101,18 @@ setDocument = Sizzle.setDocument = function( node ) { // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( document.createComment("") ); - return !div.getElementsByTagName("*").length; + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; }); // Support: IE<9 @@ -1121,32 +1120,28 @@ setDocument = Sizzle.setDocument = function( node ) { // Support: IE<10 // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, + // The broken getElementById methods don't pick up programmatically-set names, // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; return !document.getElementsByName || !document.getElementsByName( expando ).length; }); - // ID find and filter + // ID filter and find if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var m = context.getElementById( id ); - return m ? [ m ] : []; - } - }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { @@ -1155,6 +1150,36 @@ setDocument = Sizzle.setDocument = function( node ) { return node && node.value === attrId; }; }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; } // Tag @@ -1208,77 +1233,87 @@ setDocument = Sizzle.setDocument = function( node ) { // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 + // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini - assert(function( div ) { + assert(function( el ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - docElem.appendChild( div ).innerHTML = "" + + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + ""; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowcapture^='']").length ) { + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { + if ( !el.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { + if ( !el.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibing-combinator selector` fails - if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { rbuggyQSA.push(".#.+[+~]"); } }); - assert(function( div ) { + assert(function( el ) { + el.innerHTML = "" + + ""; + // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = document.createElement("input"); input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); + el.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { + if ( el.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); + el.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } @@ -1289,14 +1324,14 @@ setDocument = Sizzle.setDocument = function( node ) { docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { - assert(function( div ) { + assert(function( el ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); + support.disconnectedMatch = matches.call( el, "*" ); // This should fail with an exception // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); + matches.call( el, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } @@ -1498,6 +1533,10 @@ Sizzle.attr = function( elem, name ) { null; }; +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; @@ -1965,13 +2004,8 @@ Expr = Sizzle.selectors = { }, // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements @@ -2173,7 +2207,9 @@ function toSelector( tokens ) { function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", doneName = done++; return combinator.first ? @@ -2184,6 +2220,7 @@ function addCombinator( matcher, combinator, base ) { return matcher( elem, context, xml ); } } + return false; } : // Check against all ancestor/preceding elements @@ -2209,14 +2246,16 @@ function addCombinator( matcher, combinator, base ) { // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - if ( (oldCache = uniqueCache[ dir ]) && + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements - uniqueCache[ dir ] = newCache; + uniqueCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { @@ -2226,6 +2265,7 @@ function addCombinator( matcher, combinator, base ) { } } } + return false; }; } @@ -2588,8 +2628,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { // Reduce context if the leading compound selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { @@ -2659,17 +2698,17 @@ setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { +support.sortDetached = assert(function( el ) { // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; }) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { @@ -2680,10 +2719,10 @@ if ( !assert(function( div ) { // Support: IE<9 // Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; }) ) { addHandle( "value", function( elem, name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { @@ -2694,8 +2733,8 @@ if ( !support.attributes || !assert(function( div ) { // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; }) ) { addHandle( booleans, function( elem, name, isXML ) { var val; @@ -2716,11 +2755,15 @@ return Sizzle; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; + +// Deprecated jQuery.expr[ ":" ] = jQuery.expr.pseudos; jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + @@ -2755,40 +2798,41 @@ var siblings = function( n, elem ) { var rneedsContext = jQuery.expr.match.needsContext; -var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + -var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { + if ( isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; } ); - } + // Single element if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; } ); - } - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); } - return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not; - } ); + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); } jQuery.filter = function( expr, elems, not ) { @@ -2798,19 +2842,20 @@ jQuery.filter = function( expr, elems, not ) { expr = ":not(" + expr + ")"; } - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); }; jQuery.fn.extend( { find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; + var i, ret, + len = this.length, + self = this; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter( function() { @@ -2822,14 +2867,13 @@ jQuery.fn.extend( { } ) ); } + ret = this.pushStack( [] ); + for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; + return len > 1 ? jQuery.uniqueSort( ret ) : ret; }, filter: function( selector ) { return this.pushStack( winnow( this, selector || [], false ) ); @@ -2861,7 +2905,8 @@ var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; @@ -2871,14 +2916,14 @@ var rootjQuery, return this; } - // init accepts an alternate rootjQuery + // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { - if ( selector.charAt( 0 ) === "<" && - selector.charAt( selector.length - 1 ) === ">" && + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check @@ -2895,7 +2940,7 @@ var rootjQuery, if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; - // scripts is true for back-compat + // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], @@ -2908,7 +2953,7 @@ var rootjQuery, for ( match in context ) { // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { + if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes @@ -2924,23 +2969,12 @@ var rootjQuery, } else { elem = document.getElementById( match[ 2 ] ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { + if ( elem ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[ 2 ] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; + // Inject the element directly into the jQuery object this[ 0 ] = elem; + this.length = 1; } - - this.context = document; - this.selector = selector; return this; } @@ -2956,25 +2990,20 @@ var rootjQuery, // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { - this.context = this[ 0 ] = selector; + this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof root.ready !== "undefined" ? + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - return jQuery.makeArray( selector, this ); }; @@ -2987,7 +3016,7 @@ rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set + // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, @@ -2997,12 +3026,12 @@ var rparentsprev = /^(?:parents|prev(?:Until|All))/, jQuery.fn.extend( { has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; + var targets = jQuery( target, this ), + l = targets.length; return this.filter( function() { - for ( i = 0; i < len; i++ ) { + var i = 0; + for ( ; i < l; i++ ) { if ( jQuery.contains( this, targets[ i ] ) ) { return true; } @@ -3015,23 +3044,24 @@ jQuery.fn.extend( { i = 0, l = this.length, matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; + targets = typeof selectors !== "string" && jQuery( selectors ); - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && ( pos ? - pos.index( cur ) > -1 : + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { - matched.push( cur ); - break; + matched.push( cur ); + break; + } } } } @@ -3039,8 +3069,7 @@ jQuery.fn.extend( { return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, - // Determine the position of an element within - // the matched set of elements + // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent @@ -3048,16 +3077,17 @@ jQuery.fn.extend( { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } - // index in selector + // Index in selector if ( typeof elem === "string" ) { - return jQuery.inArray( this[ 0 ], jQuery( elem ) ); + return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element - return jQuery.inArray( + return indexOf.call( this, // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem, this ); + elem.jquery ? elem[ 0 ] : elem + ); }, add: function( selector, context ) { @@ -3076,10 +3106,7 @@ jQuery.fn.extend( { } ); function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && cur.nodeType !== 1 ); - + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } @@ -3119,46 +3146,55 @@ jQuery.each( { return siblings( elem.firstChild ); }, contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); + var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); + matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { - ret = jQuery.uniqueSort( ret ); + jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { - ret = ret.reverse(); + matched.reverse(); } } - return this.pushStack( ret ); + return this.pushStack( matched ); }; } ); -var rnotwhite = ( /\S+/g ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); // Convert String-formatted options into Object-formatted ones function createOptions( options ) { var object = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { object[ flag ] = true; } ); return object; @@ -3219,7 +3255,7 @@ jQuery.Callbacks = function( options ) { fire = function() { // Enforce single-firing - locked = options.once; + locked = locked || options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes @@ -3275,11 +3311,11 @@ jQuery.Callbacks = function( options ) { ( function add( args ) { jQuery.each( args, function( _, arg ) { - if ( jQuery.isFunction( arg ) ) { + if ( isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } - } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + } else if ( arg && arg.length && toType( arg ) !== "string" ) { // Inspect recursively add( arg ); @@ -3342,9 +3378,9 @@ jQuery.Callbacks = function( options ) { // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions lock: function() { - locked = true; - if ( !memory ) { - self.disable(); + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; } return this; }, @@ -3381,15 +3417,59 @@ jQuery.Callbacks = function( options ) { }; +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + jQuery.extend( { Deferred: function( func ) { var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], - [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { @@ -3400,23 +3480,33 @@ jQuery.extend( { deferred.done( arguments ).fail( arguments ); return this; }, - then: function( /* fnDone, fnFail, fnProgress */ ) { + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; + return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { + if ( returned && isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( - this === promise ? newDefer.promise() : this, + this, fn ? [ returned ] : arguments ); } @@ -3425,6 +3515,170 @@ jQuery.extend( { fns = null; } ).promise(); }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object @@ -3434,33 +3688,58 @@ jQuery.extend( { }, deferred = {}; - // Keep pipe for back-compat - promise.pipe = promise.then; - // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], - stateString = tuple[ 3 ]; + stateString = tuple[ 5 ]; - // promise[ done | fail | progress ] = list.add + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { - list.add( function() { + list.add( + function() { - // state = [ resolved | rejected ] - state = stateString; + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); } - // deferred[ resolve | reject | notify ] + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); @@ -3477,69 +3756,95 @@ jQuery.extend( { }, // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), resolveValues = slice.call( arguments ), - length = resolveValues.length, - // the count of uncompleted subordinates - remaining = length !== 1 || - ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + // the master Deferred + master = jQuery.Deferred(), - // the master Deferred. - // If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { + // subordinate callback factory + updateFunc = function( i ) { return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); - - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); } }; - }, + }; - progressValues, progressContexts, resolveContexts; + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .progress( updateFunc( i, progressContexts, progressValues ) ) - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ); - } else { - --remaining; - } + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); } } - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } - return deferred.promise(); + return master.promise(); } } ); +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + // The deferred used on DOM ready -var readyList; +var readyList = jQuery.Deferred(); jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); return this; }; @@ -3553,15 +3858,6 @@ jQuery.extend( { // the ready event fires. See #6781 readyWait: 1, - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - // Handle when the DOM is ready ready: function( wait ) { @@ -3580,468 +3876,369 @@ jQuery.extend( { // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } } } ); -/** - * Clean-up method for dom ready events - */ -function detach() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); +jQuery.ready.then = readyList.then; - } else { - document.detachEvent( "onreadystatechange", completed ); - window.detachEvent( "onload", completed ); - } -} - -/** - * The ready event handler and self cleanup method - */ +// The ready event handler and self cleanup method function completed() { - - // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || - window.event.type === "load" || - document.readyState === "complete" ) { - - detach(); - jQuery.ready(); - } + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); } -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - readyList = jQuery.Deferred(); + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); - // Catch cases where $(document).ready() is called - // after the browser event has already occurred. - // Support: IE6-10 - // Older IE sometimes signals "interactive" too soon - if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { +} else { - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", completed ); +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; - // A fallback to window.onload, that will always work - window.attachEvent( "onload", completed ); + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } - // If IE and not a frame - // continually check to see if the document is ready - var top = false; + // Sets one value + } else if ( value !== undefined ) { + chainable = true; - try { - top = window.frameElement == null && document.documentElement; - } catch ( e ) {} + if ( !isFunction( value ) ) { + raw = true; + } - if ( top && top.doScroll ) { - ( function doScrollCheck() { - if ( !jQuery.isReady ) { + if ( bulk ) { - try { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll( "left" ); - } catch ( e ) { - return window.setTimeout( doScrollCheck, 50 ); - } + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } - // detach all dom ready events - detach(); - - // and execute any waiting functions - jQuery.ready(); - } - } )(); + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); } } } - return readyList.promise( obj ); + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; }; -// Kick off the DOM ready check even if the user does not -jQuery.ready.promise(); +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; - - -// Support: IE<9 -// Iteration over object's inherited properties before its own -var i; -for ( i in jQuery( support ) ) { - break; +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); } -support.ownFirst = i === "0"; -// Note: most support tests are defined in their respective modules. -// false until the test is run -support.inlineBlockNeedsLayout = false; +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { -// Execute ASAP in case we need to set body.style.zoom -jQuery( function() { - - // Minified: var a,b,c,d - var val, div, body, container; - - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - - // Return for frameset docs that don't have a body - return; - } - - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); - - if ( typeof div.style.zoom !== "undefined" ) { - - // Support: IE<8 - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; - - support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; - if ( val ) { - - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - // Support: IE<8 - body.style.zoom = 1; - } - } - - body.removeChild( container ); -} ); - - -( function() { - var div = document.createElement( "div" ); - - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch ( e ) { - support.deleteExpando = false; - } - - // Null elements to avoid leaks in IE. - div = null; -} )(); -var acceptData = function( elem ) { - var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ], - nodeType = +elem.nodeType || 1; - - // Do not set data on non-element DOM nodes because it will not be cleared (#8335). - return nodeType !== 1 && nodeType !== 9 ? - false : - - // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute( "classid" ) === noData; + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; + rmultiDash = /[A-Z]/g; -function dataAttr( elem, key, data ) { +function getData( data ) { + if ( data === "true" ) { + return true; + } - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { + if ( data === "false" ) { + return false; + } - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + if ( data === "null" ) { + return null; + } - data = elem.getAttribute( name ); + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } + if ( rbrace.test( data ) ) { + return JSON.parse( data ); } return data; } -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { +function dataAttr( elem, key, data ) { var name; - for ( name in obj ) { - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); - return true; -} + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} -function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !acceptData( elem ) ) { - return; - } - - var ret, thisCache, - internalKey = jQuery.expando, - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && - data === undefined && typeof name === "string" ) { - return; - } - - if ( !id ) { - - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); } else { - id = internalKey; + data = undefined; } } - - if ( !cache[ id ] ) { - - // Avoid exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( typeof name === "string" ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; -} - -function internalRemoveData( elem, name, pvt ) { - if ( !acceptData( elem ) ) { - return; - } - - var thisCache, i, - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split( " " ); - } - } - } else { - - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = name.concat( jQuery.map( name, jQuery.camelCase ) ); - } - - i = name.length; - while ( i-- ) { - delete thisCache[ name[ i ] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - /* jshint eqeqeq: false */ - } else if ( support.deleteExpando || cache != cache.window ) { - /* jshint eqeqeq: true */ - delete cache[ id ]; - - // When all else fails, undefined - } else { - cache[ id ] = undefined; - } + return data; } jQuery.extend( { - cache: {}, - - // The following elements (space-suffixed to avoid Object.prototype collisions) - // throw uncatchable exceptions if you attempt to set expando properties - noData: { - "applet ": true, - "embed ": true, - - // ...but Flash objects (which have this classid) *can* handle expandos - "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - }, - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { - return internalData( elem, name, data ); + return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { - return internalRemoveData( elem, name ); + dataUser.remove( elem, name ); }, - // For internal use only. + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { - return internalData( elem, name, data, true ); + return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { - return internalRemoveData( elem, name, true ); + dataPriv.remove( elem, name ); } } ); @@ -4051,29 +4248,26 @@ jQuery.fn.extend( { elem = this[ 0 ], attrs = elem && elem.attributes; - // Special expections of .data basically thwart jQuery.access, - // so implement the relevant behavior ourselves - // Gets all values if ( key === undefined ) { if ( this.length ) { - data = jQuery.data( elem ); + data = dataUser.get( elem ); - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { - // Support: IE11+ + // Support: IE 11 only // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice( 5 ) ); + name = camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } - jQuery._data( elem, "parsedAttrs", true ); + dataPriv.set( elem, "hasDataAttrs", true ); } } @@ -4083,25 +4277,50 @@ jQuery.fn.extend( { // Sets multiple values if ( typeof key === "object" ) { return this.each( function() { - jQuery.data( this, key ); + dataUser.set( this, key ); } ); } - return arguments.length > 1 ? + return access( this, function( value ) { + var data; - // Sets one value + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... this.each( function() { - jQuery.data( this, key, value ); - } ) : - // Gets one value - // Try to fetch any internally stored data first - elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { - jQuery.removeData( this, key ); + dataUser.remove( this, key ); } ); } } ); @@ -4113,12 +4332,12 @@ jQuery.extend( { if ( elem ) { type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); + queue = dataPriv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } @@ -4152,7 +4371,7 @@ jQuery.extend( { queue.unshift( "inprogress" ); } - // clear up the last queue stop function + // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } @@ -4162,14 +4381,12 @@ jQuery.extend( { } }, - // not intended for public consumption - generates a queueHooks object, - // or returns the current one + // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { empty: jQuery.Callbacks( "once memory" ).add( function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); + dataPriv.remove( elem, [ type + "queue", key ] ); } ) } ); } @@ -4194,7 +4411,7 @@ jQuery.fn.extend( { this.each( function() { var queue = jQuery.queue( this, type, data ); - // ensure a hooks for this queue + // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { @@ -4232,7 +4449,7 @@ jQuery.fn.extend( { type = type || "fx"; while ( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); @@ -4242,57 +4459,6 @@ jQuery.fn.extend( { return defer.promise( obj ); } } ); - - -( function() { - var shrinkWrapBlocksVal; - - support.shrinkWrapBlocks = function() { - if ( shrinkWrapBlocksVal != null ) { - return shrinkWrapBlocksVal; - } - - // Will be changed later if needed. - shrinkWrapBlocksVal = false; - - // Minified: var b,c,d - var div, body, container; - - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - - // Test fired too early or in an unsupported environment, exit. - return; - } - - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); - - // Support: IE6 - // Check if elements with layout shrink-wrap their children - if ( typeof div.style.zoom !== "undefined" ) { - - // Reset CSS: box-sizing; display; margin; border - div.style.cssText = - - // Support: Firefox<29, Android 2.3 - // Vendor-prefix box-sizing - "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + - "box-sizing:content-box;display:block;margin:0;border:0;" + - "padding:1px;width:1px;zoom:1"; - div.appendChild( document.createElement( "div" ) ).style.width = "5px"; - shrinkWrapBlocksVal = div.offsetWidth !== 3; - } - - body.removeChild( container ); - - return shrinkWrapBlocksVal; - }; - -} )(); var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); @@ -4300,24 +4466,58 @@ var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; -var isHidden = function( elem, el ) { +var isHiddenWithinTree = function( elem, el ) { - // isHidden might be called from jQuery#filter function; + // isHiddenWithinTree might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || - !jQuery.contains( elem.ownerDocument, elem ); + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; }; +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, - scale = 1, + var adjusted, scale, maxIterations = 20, currentValue = tween ? - function() { return tween.cur(); } : - function() { return jQuery.css( elem, prop, "" ); }, + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, initial = currentValue(), unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), @@ -4327,30 +4527,33 @@ function adjustCSS( elem, prop, valueParts, tween ) { if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + // Trust units reported by jQuery.css unit = unit || initialInUnit[ 3 ]; - // Make sure we update the tween properties later on - valueParts = valueParts || []; - // Iteratively approximate from a nonzero starting point initialInUnit = +initial || 1; - do { + while ( maxIterations-- ) { - // If previous iteration zeroed out, double until we get *something*. - // Use string for doubling so we don't accidentally see scale as unchanged below - scale = scale || ".5"; - - // Adjust and apply - initialInUnit = initialInUnit / scale; + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; - // Update scale, tolerating zero or NaN from tween.cur() - // Break the loop if scale is unchanged or perfect, or if we've just had enough. - } while ( - scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations - ); + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; } if ( valueParts ) { @@ -4370,175 +4573,126 @@ function adjustCSS( elem, prop, valueParts, tween ) { } -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; +var defaultDisplayMap = {}; - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( - elems[ i ], - key, - raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } + if ( display ) { + return display; } - return chainable ? - elems : + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[ 0 ], key ) : emptyGet; -}; -var rcheckableType = ( /^(?:checkbox|radio)$/i ); + temp.parentNode.removeChild( temp ); -var rtagName = ( /<([\w:-]+)/ ); - -var rscriptType = ( /^$|\/(?:java|ecma)script/i ); - -var rleadingWhitespace = ( /^\s+/ ); - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" + - "details|dialog|figcaption|figure|footer|header|hgroup|main|" + - "mark|meter|nav|output|picture|progress|section|summary|template|time|video"; - - - -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } + if ( display === "none" ) { + display = "block"; } - return safeFrag; + defaultDisplayMap[ nodeName ] = display; + + return display; } +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; -( function() { - var div = document.createElement( "div" ), - fragment = document.createDocumentFragment(), - input = document.createElement( "input" ); + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } - // Setup - div.innerHTML = "
a"; + display = elem.style.display; + if ( show ) { - // IE strips leading whitespace when .innerHTML is used - support.leadingWhitespace = div.firstChild.nodeType === 3; + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - support.tbody = !div.getElementsByTagName( "tbody" ).length; + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - support.html5Clone = - document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + return elements; +} - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - input.type = "checkbox"; - input.checked = true; - fragment.appendChild( input ); - support.appendChecked = input.checked; +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE6-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); - // #11217 - WebKit loses check when the name is after the checked attribute - fragment.appendChild( div ); +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input = document.createElement( "input" ); - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - div.appendChild( input ); - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE<9 - // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+ - support.noCloneEvent = !!div.addEventListener; - - // Support: IE<9 - // Since attributes and properties are the same in IE, - // cleanData must set properties to undefined rather than use removeAttribute - div[ jQuery.expando ] = 1; - support.attributes = !div.getAttribute( jQuery.expando ); -} )(); // We have to close these tags to support XHTML (#13200) var wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
", "
" ], - area: [ 1, "", "" ], - // Support: IE8 - param: [ 1, "", "" ], + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], tr: [ 2, "", "
" ], - col: [ 2, "", "
" ], td: [ 3, "", "
" ], - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + _default: [ 0, "", "" ] }; -// Support: IE8-IE9 +// Support: IE <=9 only wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; @@ -4546,66 +4700,52 @@ wrapMap.th = wrapMap.td; function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== "undefined" ? - context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== "undefined" ? - context.querySelectorAll( tag || "*" ) : - undefined; - if ( !found ) { - for ( found = [], elems = context.childNodes || context; - ( elem = elems[ i ] ) != null; - i++ - ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); - } - } + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; } - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; ( elem = elems[ i ] ) != null; i++ ) { - jQuery._data( - elem, + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], "globalEval", - !refElements || jQuery._data( refElements[ i ], "globalEval" ) + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) ); } } -var rhtml = /<|&#?\w+;/, - rtbody = / from table fragments - if ( !support.tbody ) { - - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : - - // String was a bare or - wrap[ 1 ] === "
" && !rtbody.test( elem ) ? - tmp : - 0; - - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && - !tbody.childNodes.length ) { - - elem.removeChild( tbody ); - } - } - } - + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); - // Fix #12392 for WebKit and IE > 9 + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; - - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); - } - - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; } } } - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); - } + // Remove wrapper from fragment + fragment.textContent = ""; i = 0; while ( ( elem = nodes[ i++ ] ) ) { @@ -4698,14 +4802,13 @@ function buildFragment( elems, context, scripts, selection, ignored ) { if ( ignored ) { ignored.push( elem ); } - continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); + tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { @@ -4723,37 +4826,41 @@ function buildFragment( elems, context, scripts, selection, ignored ) { } } - tmp = null; - - return safe; + return fragment; } ( function() { - var i, eventName, - div = document.createElement( "div" ); + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); - // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events) - for ( i in { submit: true, change: true, focusin: true } ) { - eventName = "on" + i; + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); - if ( !( support[ i ] = eventName in window ) ) { + div.appendChild( input ); - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - div.setAttribute( eventName, "t" ); - support[ i ] = div.attributes[ eventName ].expando === false; - } - } + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - // Null elements to avoid leaks in IE. - div = null; + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; } )(); +var documentElement = document.documentElement; -var rformElems = /^(?:input|select|textarea)$/i, + +var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { @@ -4764,7 +4871,7 @@ function returnFalse() { return false; } -// Support: IE9 +// Support: IE <=9 only // See #13393 for more info function safeActiveElement() { try { @@ -4842,10 +4949,11 @@ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { - var tmp, events, t, handleObjIn, - special, eventHandle, handleObj, - handlers, type, namespaces, origType, - elemData = jQuery._data( elem ); + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { @@ -4859,6 +4967,12 @@ jQuery.event = { selector = handleObjIn.selector; } + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; @@ -4873,19 +4987,13 @@ jQuery.event = { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && - ( !e || jQuery.event.triggered !== e.type ) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; - - // Add elem as a property of the handle fn to prevent a memory leak - // with IE non-native events - eventHandle.elem = elem; } // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; @@ -4923,16 +5031,12 @@ jQuery.event = { handlers = events[ type ] = []; handlers.delegateCount = 0; - // Only use addEventListener/attachEvent if the special events handler returns false + // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); + elem.addEventListener( type, eventHandle ); } } } @@ -4956,24 +5060,22 @@ jQuery.event = { jQuery.event.global[ type ] = true; } - // Nullify elem to prevent memory leaks in IE - elem = null; }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { - var j, handleObj, tmp, - origCount, t, events, - special, handlers, type, - namespaces, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); if ( !elemData || !( events = elemData.events ) ) { return; } // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; @@ -5028,174 +5130,29 @@ jQuery.event = { } } - // Remove the expando if it's no longer used + // Remove data and the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); + dataPriv.remove( elem, "handle events" ); } }, - trigger: function( event, data, elem, onlyHandlers ) { - var handle, ontype, cur, - bubbleType, special, tmp, i, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && - jQuery._data( cur, "handle" ); - - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( - ( !special._default || - special._default.apply( eventPath.pop(), data ) === false - ) && acceptData( elem ) - ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - try { - elem[ type ](); - } catch ( e ) { - - // IE<9 dies on focus/blur to hidden element (#1486,#12518) - // only reproducible on winXP IE8 native, not IE9 in IE8 mode - } - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { + dispatch: function( nativeEvent ) { // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); + var event = jQuery.event.fix( nativeEvent ); - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired @@ -5244,160 +5201,95 @@ jQuery.event = { }, handlers: function( event, handlers ) { - var i, matches, sel, handleObj, + var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; - // Support (at least): Chrome, IE9 // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // - // Support: Firefox<=42+ - // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) - if ( delegateCount && cur.nodeType && - ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + if ( delegateCount && - /* jshint eqeqeq: false */ - for ( ; cur != this; cur = cur.parentNode || this ) { - /* jshint eqeqeq: true */ + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { - matches = []; + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } - if ( matches[ sel ] ) { - matches.push( handleObj ); + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); } } - if ( matches.length ) { - handlerQueue.push( { elem: cur, handlers: matches } ); + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); } } } } // Add the remaining (directly-bound) handlers + cur = this; if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; }, - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: IE<9 - // Fix target property (#1925) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Support: Safari 6-8+ - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Support: IE<9 - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) - event.metaKey = !!event.metaKey; - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); }, - // Includes some event props shared by KeyEvent and MouseEvent - props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + - "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split( " " ), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: ( "button buttons clientX clientY fromElement offsetX offsetY " + - "pageX pageY screenX screenY toElement" ).split( " " ), - filter: function( event, original ) { - var body, eventDoc, doc, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + - ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + - ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? - original.toElement : - fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); }, special: { @@ -5411,15 +5303,8 @@ jQuery.event = { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { - try { - this.focus(); - return false; - } catch ( e ) { - - // Support: IE<9 - // If we error on focus to hidden element (#1486, #12518), - // let .trigger() run the handlers - } + this.focus(); + return false; } }, delegateType: "focusin" @@ -5437,7 +5322,7 @@ jQuery.event = { // For checkbox, fire native event so checked state will be right trigger: function() { - if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { this.click(); return false; } @@ -5445,7 +5330,7 @@ jQuery.event = { // For cross-browser consistency, don't fire native .click() on links _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); + return nodeName( event.target, "a" ); } }, @@ -5459,59 +5344,16 @@ jQuery.event = { } } } - }, - - // Piggyback on a donor event to simulate a different one - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - - // Previously, `originalEvent: {}` was set here, so stopPropagation call - // would not be triggered on donor event, since in our own - // jQuery.event.stopPropagation function we had a check for existence of - // originalEvent.stopPropagation method, so, consequently it would be a noop. - // - // Guard for simulated events was moved to jQuery.event.stopPropagation function - // since `originalEvent` should point to the original event for the - // constancy with other events and for more focused logic - } - ); - - jQuery.event.trigger( e, null, elem ); - - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } } }; -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { +jQuery.removeEvent = function( elem, type, handle ) { - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; - - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, - // to properly expose it to GC - if ( typeof elem[ name ] === "undefined" ) { - elem[ name ] = null; - } - - elem.detachEvent( name, handle ); - } - }; + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; jQuery.Event = function( src, props ) { @@ -5530,11 +5372,21 @@ jQuery.Event = function( src, props ) { this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && - // Support: IE < 9, Android < 4.0 + // Support: Android <=2.3 only src.returnValue === false ? returnTrue : returnFalse; + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + // Event type } else { this.type = src; @@ -5546,36 +5398,28 @@ jQuery.Event = function( src, props ) { } // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); + this.timeStamp = src && src.timeStamp || Date.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, + isSimulated: false, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; - if ( !e ) { - return; - } - // If preventDefault exists, run it on the original event - if ( e.preventDefault ) { + if ( e && !this.isSimulated ) { e.preventDefault(); - - // Support: IE - // Otherwise set the returnValue property of the original event to false - } else { - e.returnValue = false; } }, stopPropagation: function() { @@ -5583,25 +5427,16 @@ jQuery.Event.prototype = { this.isPropagationStopped = returnTrue; - if ( !e || this.isSimulated ) { - return; - } - - // If stopPropagation exists, run it on the original event - if ( e.stopPropagation ) { + if ( e && !this.isSimulated ) { e.stopPropagation(); } - - // Support: IE - // Set the cancelBubble property of the original event to true - e.cancelBubble = true; }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; - if ( e && e.stopImmediatePropagation ) { + if ( e && !this.isSimulated ) { e.stopImmediatePropagation(); } @@ -5609,13 +5444,74 @@ jQuery.Event.prototype = { } }; +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + // Create mouseenter/leave events using mouseover/out and event-time checks // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout // // Support: Safari 7 only // Safari sends mouseenter too often; see: -// https://code.google.com/p/chromium/issues/detail?id=470258 +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 // for the description of the bug (it existed in older Chrome versions as well). jQuery.each( { mouseenter: "mouseover", @@ -5645,171 +5541,6 @@ jQuery.each( { }; } ); -// IE submit delegation -if ( !support.submit ) { - - jQuery.event.special.submit = { - setup: function() { - - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? - - // Support: IE <=8 - // We use jQuery.prop instead of elem.form - // to allow fixing the IE8 delegated submit issue (gh-2332) - // by 3rd party polyfills/workarounds. - jQuery.prop( elem, "form" ) : - undefined; - - if ( form && !jQuery._data( form, "submit" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submitBubble = true; - } ); - jQuery._data( form, "submit", true ); - } - } ); - - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - - // If form was submitted by the user, bubble the event up the tree - if ( event._submitBubble ) { - delete event._submitBubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event ); - } - } - }, - - teardown: function() { - - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !support.change ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._justChanged = true; - } - } ); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._justChanged && !event.isTrigger ) { - this._justChanged = false; - } - - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event ); - } ); - } - return false; - } - - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event ); - } - } ); - jQuery._data( elem, "change", true ); - } - } ); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || - ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { - - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Support: Firefox -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome, Safari -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - jQuery._removeData( doc, fix ); - } else { - jQuery._data( doc, fix, attaches ); - } - } - }; - } ); -} - jQuery.fn.extend( { on: function( types, selector, data, fn ) { @@ -5853,154 +5584,97 @@ jQuery.fn.extend( { return this.each( function() { jQuery.event.remove( this, types, fn, selector ); } ); - }, - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } } } ); -var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, +var - // Support: IE 10-11, Edge 10240+ + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only // In IE/Edge using regex groups here causes severe slowdowns. // See https://connect.microsoft.com/IE/feedback/details/1736512/ rnoInnerhtml = /\s*$/g, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); + rcleanScript = /^\s*\s*$/g; -// Support: IE<8 -// Manipulating tables requires a tbody +// Prefer a tbody over its parent table for containing new rows function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - elem.getElementsByTagName( "tbody" )[ 0 ] || - elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : - elem; + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { - elem.type = ( jQuery.find.attr( elem, "type" ) !== null ) + "/" + elem.type; + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - if ( match ) { - elem.type = match[ 1 ]; + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); } else { elem.removeAttribute( "type" ); } + return elem; } function cloneCopyEvent( src, dest ) { - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} - -function fixCloneNodeIssues( src, dest ) { - var nodeName, e, data; - - // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; } - nodeName = dest.nodeName.toLowerCase(); + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; - // IE6-8 copies events bound via attachEvent when using cloneNode. - if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { - data = jQuery._data( dest ); + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; - for ( e in data.events ) { - jQuery.removeEvent( dest, e, data.handle ); + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } } - - // Event data gets referenced instead of copied if the expando gets copied too - dest.removeAttribute( jQuery.expando ); } - // IE blanks contents when cloning scripts, and tries to evaluate newly-set text - if ( nodeName === "script" && dest.text !== src.text ) { - disableScript( dest ).text = src.text; - restoreScript( dest ); + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - } else if ( nodeName === "object" ) { - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } + dataUser.set( dest, udataCur ); + } +} - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { - dest.innerHTML = src.innerHTML; - } +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.defaultSelected = dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields + // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } @@ -6011,21 +5685,20 @@ function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays args = concat.apply( [], args ); - var first, node, hasScripts, - scripts, doc, fragment, + var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[ 0 ], - isFunction = jQuery.isFunction( value ); + valueIsFunction = isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || + if ( valueIsFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test( value ) ) ) { return collection.each( function( index ) { var self = collection.eq( index ); - if ( isFunction ) { + if ( valueIsFunction ) { args[ 0 ] = value.call( this, index, self.html() ); } domManip( self, args, callback, ignored ); @@ -6057,7 +5730,7 @@ function domManip( collection, args, callback, ignored ) { // Keep references to cloned scripts for later restoration if ( hasScripts ) { - // Support: Android<4.1, PhantomJS<2 + // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, "script" ) ); } @@ -6076,27 +5749,21 @@ function domManip( collection, args, callback, ignored ) { for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && + !dataPriv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - if ( node.src ) { + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ) { jQuery._evalUrl( node.src ); } } else { - jQuery.globalEval( - ( node.text || node.textContent || node.innerHTML || "" ) - .replace( rcleanScript, "" ) - ); + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); } } } } - - // Fix #11809: Avoid leaking memory - fragment = first = null; } } @@ -6105,11 +5772,10 @@ function domManip( collection, args, callback, ignored ) { function remove( elem, selector, keepData ) { var node, - elems = selector ? jQuery.filter( selector, elem ) : elem, + nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; - for ( ; ( node = elems[ i ] ) != null; i++ ) { - + for ( ; ( node = nodes[ i ] ) != null; i++ ) { if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } @@ -6131,34 +5797,20 @@ jQuery.extend( { }, clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, node, clone, i, srcElements, + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), inPage = jQuery.contains( elem.ownerDocument, elem ); - if ( support.html5Clone || jQuery.isXMLDoc( elem ) || - !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { - clone = elem.cloneNode( true ); - - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } - - if ( ( !support.noCloneEvent || !support.noCloneChecked ) && - ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); - // Fix all IE cloning issues - for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { - - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[ i ] ) { - fixCloneNodeIssues( node, destElements[ i ] ); - } + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); } } @@ -6168,8 +5820,8 @@ jQuery.extend( { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); - for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { - cloneCopyEvent( node, destElements[ i ] ); + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); @@ -6182,27 +5834,18 @@ jQuery.extend( { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } - destElements = srcElements = node = null; - // Return the cloned set return clone; }, - cleanData: function( elems, /* internal */ forceAcceptData ) { - var elem, type, id, data, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - attributes = support.attributes, - special = jQuery.event.special; + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; - for ( ; ( elem = elems[ i ] ) != null; i++ ) { - if ( forceAcceptData || acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { @@ -6215,27 +5858,15 @@ jQuery.extend( { } } - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { - delete cache[ id ]; - - // Support: IE<9 - // IE does not allow us to delete expando properties from nodes - // IE creates expando attributes along with the property - // IE does not have a removeAttribute function on Document nodes - if ( !attributes && typeof elem.removeAttribute !== "undefined" ) { - elem.removeAttribute( internalKey ); - - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://code.google.com/p/chromium/issues/detail?id=378607 - } else { - elem[ internalKey ] = undefined; - } - - deletedIds.push( id ); - } + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; } } } @@ -6243,10 +5874,6 @@ jQuery.extend( { } ); jQuery.fn.extend( { - - // Keep domManip exposed until 3.0 (gh-2225) - domManip: domManip, - detach: function( selector ) { return remove( this, selector, true ); }, @@ -6259,9 +5886,11 @@ jQuery.fn.extend( { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : - this.empty().append( - ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) - ); + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); }, null, value, arguments.length ); }, @@ -6304,21 +5933,13 @@ jQuery.fn.extend( { i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { - - // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { + + // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); - } - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - - // If this is a select, ensure that it displays empty (#12336) - // Support: IE<9 - if ( elem.options && jQuery.nodeName( elem, "select" ) ) { - elem.options.length = 0; + // Remove any remaining nodes + elem.textContent = ""; } } @@ -6340,25 +5961,21 @@ jQuery.fn.extend( { i = 0, l = this.length; - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = jQuery.htmlPrefilter( value ); try { for ( ; i < l; i++ ) { + elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks - elem = this[ i ] || {}; if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; @@ -6405,298 +6022,28 @@ jQuery.each( { }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, - i = 0, ret = [], insert = jQuery( selector ), - last = insert.length - 1; + last = insert.length - 1, + i = 0; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems ); - // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; } ); - - -var iframe, - elemdisplay = { - - // Support: Firefox - // We have to pre-define these values for FF (#10227) - HTML: "block", - BODY: "block" - }; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ - -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - display = jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = ( iframe || jQuery( "