diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index b4f5b7c73a6..c2c23ebab23 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -370,3 +370,4 @@ f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
+b30ae794d974d7dd3eb4e84203f70021823fa6c6 jdk-9+128
diff --git a/common/autoconf/boot-jdk.m4 b/common/autoconf/boot-jdk.m4
index 30dcf844a88..d3fd298d704 100644
--- a/common/autoconf/boot-jdk.m4
+++ b/common/autoconf/boot-jdk.m4
@@ -345,6 +345,9 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
# Disable special log output when a debug build is used as Boot JDK...
ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA])
+ # Force en-US environment
+ ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
+
# Apply user provided options.
ADD_JVM_ARG_IF_OK([$with_boot_jdk_jvmargs],boot_jdk_jvmargs,[$JAVA])
diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh
index e8b5461b512..961e90e1142 100644
--- a/common/autoconf/generated-configure.sh
+++ b/common/autoconf/generated-configure.sh
@@ -5094,7 +5094,7 @@ VS_SDK_PLATFORM_NAME_2013=
#CUSTOM_AUTOCONF_INCLUDE
# Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1467960715
+DATE_WHEN_GENERATED=1469202305
###############################################################################
#
@@ -65048,6 +65048,23 @@ $as_echo_n "checking flags for boot jdk java command ... " >&6; }
fi
+ # Force en-US environment
+
+ $ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5
+ $ECHO "Command: $JAVA -Duser.language=en -Duser.country=US -version" >&5
+ OUTPUT=`$JAVA -Duser.language=en -Duser.country=US -version 2>&1`
+ FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn`
+ FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""`
+ if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then
+ boot_jdk_jvmargs="$boot_jdk_jvmargs -Duser.language=en -Duser.country=US"
+ JVM_ARG_OK=true
+ else
+ $ECHO "Arg failed:" >&5
+ $ECHO "$OUTPUT" >&5
+ JVM_ARG_OK=false
+ fi
+
+
# Apply user provided options.
$ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5
diff --git a/corba/.hgtags b/corba/.hgtags
index 2667a3cc368..d899bc525b9 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -370,3 +370,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
+1f093d3f8cd99cd37c3b0af4cf5c3bffaa9c8b98 jdk-9+128
diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java
index bb08fd7c77c..6a3fd5ca305 100644
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java
+++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java
@@ -1,5 +1,4 @@
/*
- *
* Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -22,7 +21,6 @@
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
- *
*/
package com.sun.corba.se.impl.activation;
diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java
index 41dba4d9489..a368ee9d30e 100644
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java
+++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java
@@ -34,21 +34,13 @@ import java.security.PermissionCollection;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.Collection;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Enumeration;
-import java.util.Properties;
-import java.util.IdentityHashMap;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;
@@ -165,8 +157,18 @@ public final class ORBUtility {
* Return default ValueHandler
*/
public static ValueHandler createValueHandler() {
+ ValueHandler vh;
+ try {
+ vh = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public ValueHandler run() throws Exception {
return Util.createValueHandler();
}
+ });
+ } catch (PrivilegedActionException e) {
+ throw new InternalError(e.getCause());
+ }
+ return vh;
+ }
/**
* Returns true if it was accurately determined that the remote ORB is
@@ -664,7 +666,16 @@ public final class ORBUtility {
* ValueHandler.
*/
public static byte getMaxStreamFormatVersion() {
- ValueHandler vh = Util.createValueHandler();
+ ValueHandler vh;
+ try {
+ vh = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public ValueHandler run() throws Exception {
+ return Util.createValueHandler();
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw new InternalError(e.getCause());
+ }
if (!(vh instanceof javax.rmi.CORBA.ValueHandlerMultiFormat))
return ORBConstants.STREAM_FORMAT_VERSION_1;
diff --git a/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java b/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java
index b8473ce31e7..03a40734ed2 100644
--- a/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java
+++ b/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,6 +45,7 @@ import javax.rmi.CORBA.Tie;
import java.rmi.Remote;
import java.io.File;
import java.io.FileInputStream;
+import java.io.SerializablePermission;
import java.net.MalformedURLException ;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -195,6 +196,8 @@ public class Util {
*/
public static ValueHandler createValueHandler() {
+ isCustomSerializationPermitted();
+
if (utilDelegate != null) {
return utilDelegate.createValueHandler();
}
@@ -337,6 +340,7 @@ public class Util {
// security reasons. If you know a better solution how to share this code
// then remove it from PortableRemoteObject. Also in Stub.java
private static Object createDelegate(String classKey) {
+
String className = (String)
AccessController.doPrivileged(new GetPropertyAction(classKey));
if (className == null) {
@@ -345,7 +349,6 @@ public class Util {
className = props.getProperty(classKey);
}
}
-
if (className == null) {
return new com.sun.corba.se.impl.javax.rmi.CORBA.Util();
}
@@ -389,4 +392,14 @@ public class Util {
new GetORBPropertiesFileAction());
}
+ private static void isCustomSerializationPermitted() {
+ SecurityManager sm = System.getSecurityManager();
+ if ( sm != null) {
+ // check that a serialization permission has been
+ // set to allow the loading of the Util delegate
+ // which provides access to custom ValueHandler
+ sm.checkPermission(new SerializablePermission(
+ "enableCustomValueHandler"));
+}
+ }
}
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index 7415efbd4d7..71f4f526f73 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -530,3 +530,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
+22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128
diff --git a/hotspot/make/test/JtregNative.gmk b/hotspot/make/test/JtregNative.gmk
index 4e61967e859..e89506b1c53 100644
--- a/hotspot/make/test/JtregNative.gmk
+++ b/hotspot/make/test/JtregNative.gmk
@@ -51,6 +51,7 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC := \
$(HOTSPOT_TOPDIR)/test/compiler/floatingpoint/ \
$(HOTSPOT_TOPDIR)/test/compiler/calls \
$(HOTSPOT_TOPDIR)/test/compiler/native \
+ $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/GetNamedModule \
$(HOTSPOT_TOPDIR)/test/testlibrary/jvmti \
#
@@ -64,6 +65,7 @@ endif
ifeq ($(TOOLCHAIN_TYPE), solstudio)
BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_liboverflow := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libSimpleClassFileLoadHook := -lc
+ BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libGetNamedModuleTest := -lc
endif
BUILD_HOTSPOT_JTREG_OUTPUT_DIR := $(BUILD_OUTPUT)/support/test/hotspot/jtreg/native
diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
index 94d1889d65a..2407381d431 100644
--- a/hotspot/src/os/linux/vm/os_linux.cpp
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
@@ -1742,11 +1742,11 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
}
typedef struct {
- Elf32_Half code; // Actual value as defined in elf.h
- Elf32_Half compat_class; // Compatibility of archs at VM's sense
- char elf_class; // 32 or 64 bit
- char endianess; // MSB or LSB
- char* name; // String representation
+ Elf32_Half code; // Actual value as defined in elf.h
+ Elf32_Half compat_class; // Compatibility of archs at VM's sense
+ unsigned char elf_class; // 32 or 64 bit
+ unsigned char endianess; // MSB or LSB
+ char* name; // String representation
} arch_t;
#ifndef EM_486
diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp
index b8cbb78a159..67ca5eeb5f3 100644
--- a/hotspot/src/os/solaris/vm/os_solaris.cpp
+++ b/hotspot/src/os/solaris/vm/os_solaris.cpp
@@ -1320,36 +1320,8 @@ bool os::getTimesSecs(double* process_real_time,
}
bool os::supports_vtime() { return true; }
-
-bool os::enable_vtime() {
- int fd = ::open("/proc/self/ctl", O_WRONLY);
- if (fd == -1) {
- return false;
- }
-
- long cmd[] = { PCSET, PR_MSACCT };
- int res = ::write(fd, cmd, sizeof(long) * 2);
- ::close(fd);
- if (res != sizeof(long) * 2) {
- return false;
- }
- return true;
-}
-
-bool os::vtime_enabled() {
- int fd = ::open("/proc/self/status", O_RDONLY);
- if (fd == -1) {
- return false;
- }
-
- pstatus_t status;
- int res = os::read(fd, (void*) &status, sizeof(pstatus_t));
- ::close(fd);
- if (res != sizeof(pstatus_t)) {
- return false;
- }
- return status.pr_flags & PR_MSACCT;
-}
+bool os::enable_vtime() { return false; }
+bool os::vtime_enabled() { return false; }
double os::elapsedVTime() {
return (double)gethrvtime() / (double)hrtime_hz;
diff --git a/hotspot/src/share/vm/classfile/altHashing.cpp b/hotspot/src/share/vm/classfile/altHashing.cpp
index 326351a6637..ee6b810d1e6 100644
--- a/hotspot/src/share/vm/classfile/altHashing.cpp
+++ b/hotspot/src/share/vm/classfile/altHashing.cpp
@@ -224,7 +224,7 @@ static const jchar ONE_CHAR[] = { (jchar) 0x8180};
static const jbyte THREE_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82};
static const jbyte FOUR_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83};
static const jchar TWO_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382};
-static const jint ONE_INT[] = { 0x83828180};
+static const jint ONE_INT[] = { (jint)0x83828180};
static const jbyte SIX_BYTE[] = { (jbyte) 0x80, (jbyte) 0x81, (jbyte) 0x82, (jbyte) 0x83, (jbyte) 0x84, (jbyte) 0x85};
static const jchar THREE_CHAR[] = { (jchar) 0x8180, (jchar) 0x8382, (jchar) 0x8584};
static const jbyte EIGHT_BYTE[] = {
@@ -235,7 +235,7 @@ static const jchar FOUR_CHAR[] = {
(jchar) 0x8180, (jchar) 0x8382,
(jchar) 0x8584, (jchar) 0x8786};
-static const jint TWO_INT[] = { 0x83828180, 0x87868584};
+static const jint TWO_INT[] = { (jint)0x83828180, (jint)0x87868584};
static const juint MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3;
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index f0fdc648095..fc1774d1941 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -142,7 +142,9 @@ void ClassLoaderData::oops_do(OopClosure* f, KlassClosure* klass_closure, bool m
f->do_oop(&_class_loader);
_dependencies.oops_do(f);
- _handles->oops_do(f);
+ if (_handles != NULL) {
+ _handles->oops_do(f);
+ }
if (klass_closure != NULL) {
classes_do(klass_closure);
}
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.hpp b/hotspot/src/share/vm/classfile/compactHashtable.hpp
index 3dd870c1158..13c65319db8 100644
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp
@@ -270,6 +270,10 @@ public:
// For reading from/writing to the CDS archive
void serialize(SerializeClosure* soc);
+
+ uintx base_address() {
+ return (uintx) _base_address;
+ }
};
////////////////////////////////////////////////////////////////////////
diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp
index 9924eac1422..d6cdf2e6cb9 100644
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp
@@ -871,12 +871,17 @@ void java_lang_Class::fixup_module_field(KlassHandle k, Handle module) {
int java_lang_Class::oop_size(oop java_class) {
assert(_oop_size_offset != 0, "must be set");
- return java_class->int_field(_oop_size_offset);
+ int size = java_class->int_field(_oop_size_offset);
+ assert(size > 0, "Oop size must be greater than zero, not %d", size);
+ return size;
}
+
void java_lang_Class::set_oop_size(oop java_class, int size) {
assert(_oop_size_offset != 0, "must be set");
+ assert(size > 0, "Oop size must be greater than zero, not %d", size);
java_class->int_field_put(_oop_size_offset, size);
}
+
int java_lang_Class::static_oop_field_count(oop java_class) {
assert(_static_oop_field_count_offset != 0, "must be set");
return java_class->int_field(_static_oop_field_count_offset);
diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp
index 0d39a32a70d..fbf68e6dc52 100644
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp
@@ -275,7 +275,6 @@ class java_lang_Class : AllStatic {
static int static_oop_field_count(oop java_class);
static void set_static_oop_field_count(oop java_class, int size);
-
static GrowableArray* fixup_mirror_list() {
return _fixup_mirror_list;
}
diff --git a/hotspot/src/share/vm/classfile/modules.cpp b/hotspot/src/share/vm/classfile/modules.cpp
index cd0937267f8..d9d086a3123 100644
--- a/hotspot/src/share/vm/classfile/modules.cpp
+++ b/hotspot/src/share/vm/classfile/modules.cpp
@@ -820,6 +820,28 @@ jobject Modules::get_module_by_package_name(jobject loader, jstring package, TRA
}
+jobject Modules::get_named_module(Handle h_loader, const char* package_str, TRAPS) {
+ assert(ModuleEntryTable::javabase_defined(),
+ "Attempt to call get_named_module before java.base is defined");
+ assert(h_loader.is_null() || java_lang_ClassLoader::is_subclass(h_loader->klass()),
+ "Class loader is not a subclass of java.lang.ClassLoader");
+ assert(package_str != NULL, "the package_str should not be NULL");
+
+ if (strlen(package_str) == 0) {
+ return NULL;
+ }
+ TempNewSymbol package_sym = SymbolTable::new_symbol(package_str, CHECK_NULL);
+ const PackageEntry* const pkg_entry =
+ get_package_entry_by_name(package_sym, h_loader, THREAD);
+ const ModuleEntry* const module_entry = (pkg_entry != NULL ? pkg_entry->module() : NULL);
+
+ if (module_entry != NULL && module_entry->module() != NULL && module_entry->is_named()) {
+ return JNIHandles::make_local(THREAD, JNIHandles::resolve(module_entry->module()));
+ }
+ return NULL;
+}
+
+
// This method is called by JFR and by the above method.
jobject Modules::get_module(Symbol* package_name, Handle h_loader, TRAPS) {
const PackageEntry* const pkg_entry =
diff --git a/hotspot/src/share/vm/classfile/modules.hpp b/hotspot/src/share/vm/classfile/modules.hpp
index d8abd7552bc..2d0c3311a05 100644
--- a/hotspot/src/share/vm/classfile/modules.hpp
+++ b/hotspot/src/share/vm/classfile/modules.hpp
@@ -121,6 +121,7 @@ public:
// IllegalArgumentException is thrown if loader is neither null nor a subtype of
// java/lang/ClassLoader.
static jobject get_module_by_package_name(jobject loader, jstring package, TRAPS);
+ static jobject get_named_module(Handle h_loader, const char* package, TRAPS);
// If package is defined by loader, return the
// java.lang.reflect.Module object for the module in which the package is defined.
diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp
index ffbef4c2455..f4997a7ca70 100644
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp
@@ -238,6 +238,29 @@ Symbol* SymbolTable::lookup(int index, const char* name,
}
}
+u4 SymbolTable::encode_shared(Symbol* sym) {
+ assert(DumpSharedSpaces, "called only during dump time");
+ uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+ uintx offset = uintx(sym) - base_address;
+ assert(offset < 0x7fffffff, "sanity");
+ return u4(offset);
+}
+
+Symbol* SymbolTable::decode_shared(u4 offset) {
+ assert(!DumpSharedSpaces, "called only during runtime");
+ uintx base_address = _shared_table.base_address();
+ Symbol* sym = (Symbol*)(base_address + offset);
+
+#ifndef PRODUCT
+ const char* s = (const char*)sym->bytes();
+ int len = sym->utf8_length();
+ unsigned int hash = hash_symbol(s, len);
+ assert(sym == lookup_shared(s, len, hash), "must be shared symbol");
+#endif
+
+ return sym;
+}
+
// Pick hashing algorithm.
unsigned int SymbolTable::hash_symbol(const char* s, int len) {
return use_alternate_hashcode() ?
diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp
index 5a7db595335..e45ec753ef1 100644
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp
@@ -253,6 +253,8 @@ public:
// Sharing
static void serialize(SerializeClosure* soc);
+ static u4 encode_shared(Symbol* sym);
+ static Symbol* decode_shared(u4 offset);
// Rehash the symbol table if it gets out of balance
static void rehash_table();
diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
index 44815e72930..4da4869cef2 100644
--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
+++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
@@ -78,7 +78,19 @@ public:
TRAPS) {
return NULL;
}
+
static void serialize(SerializeClosure* soc) {}
+
+ // The (non-application) CDS implementation supports only classes in the boot
+ // class loader, which ensures that the verification constraints are the same
+ // during archive creation time and runtime. Thus we can do the constraint checks
+ // entirely during archive creation time.
+ static bool add_verification_constraint(Klass* k, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected,
+ bool from_is_array, bool from_is_object) {return false;}
+ static void finalize_verification_constraints() {}
+ static void check_verification_constraints(instanceKlassHandle klass,
+ TRAPS) {}
};
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
diff --git a/hotspot/src/share/vm/classfile/verificationType.cpp b/hotspot/src/share/vm/classfile/verificationType.cpp
index be4594f6ac5..1f0f9549013 100644
--- a/hotspot/src/share/vm/classfile/verificationType.cpp
+++ b/hotspot/src/share/vm/classfile/verificationType.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verificationType.hpp"
#include "classfile/verifier.hpp"
@@ -41,6 +42,39 @@ VerificationType VerificationType::from_tag(u1 tag) {
}
}
+bool VerificationType::resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) {
+ Klass* obj = SystemDictionary::resolve_or_fail(
+ name, Handle(THREAD, klass->class_loader()),
+ Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(obj, klass());
+ }
+
+ KlassHandle this_class(THREAD, obj);
+
+ if (this_class->is_interface() && (!from_field_is_protected ||
+ from_name != vmSymbols::java_lang_Object())) {
+ // If we are not trying to access a protected field or method in
+ // java.lang.Object then, for arrays, we only allow assignability
+ // to interfaces java.lang.Cloneable and java.io.Serializable.
+ // Otherwise, we treat interfaces as java.lang.Object.
+ return !from_is_array ||
+ this_class == SystemDictionary::Cloneable_klass() ||
+ this_class == SystemDictionary::Serializable_klass();
+ } else if (from_is_object) {
+ Klass* from_class = SystemDictionary::resolve_or_fail(
+ from_name, Handle(THREAD, klass->class_loader()),
+ Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(from_class, klass());
+ }
+ return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
+ }
+
+ return false;
+}
+
bool VerificationType::is_reference_assignable_from(
const VerificationType& from, ClassVerifier* context,
bool from_field_is_protected, TRAPS) const {
@@ -58,33 +92,17 @@ bool VerificationType::is_reference_assignable_from(
// any object or array is assignable to java.lang.Object
return true;
}
- Klass* obj = SystemDictionary::resolve_or_fail(
- name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(obj, klass());
+
+ if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass(),
+ name(), from.name(), from_field_is_protected, from.is_array(),
+ from.is_object())) {
+ // If add_verification_constraint() returns true, the resolution/check should be
+ // delayed until runtime.
+ return true;
}
- KlassHandle this_class(THREAD, obj);
-
- if (this_class->is_interface() && (!from_field_is_protected ||
- from.name() != vmSymbols::java_lang_Object())) {
- // If we are not trying to access a protected field or method in
- // java.lang.Object then, for arrays, we only allow assignability
- // to interfaces java.lang.Cloneable and java.io.Serializable.
- // Otherwise, we treat interfaces as java.lang.Object.
- return !from.is_array() ||
- this_class == SystemDictionary::Cloneable_klass() ||
- this_class == SystemDictionary::Serializable_klass();
- } else if (from.is_object()) {
- Klass* from_class = SystemDictionary::resolve_or_fail(
- from.name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(from_class, klass());
- }
- return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
- }
+ return resolve_and_check_assignability(klass(), name(), from.name(),
+ from_field_is_protected, from.is_array(), from.is_object(), THREAD);
} else if (is_array() && from.is_array()) {
VerificationType comp_this = get_component(context, CHECK_false);
VerificationType comp_from = from.get_component(context, CHECK_false);
diff --git a/hotspot/src/share/vm/classfile/verificationType.hpp b/hotspot/src/share/vm/classfile/verificationType.hpp
index c890cfb7e40..a654f48acb5 100644
--- a/hotspot/src/share/vm/classfile/verificationType.hpp
+++ b/hotspot/src/share/vm/classfile/verificationType.hpp
@@ -333,6 +333,12 @@ class VerificationType VALUE_OBJ_CLASS_SPEC {
bool is_reference_assignable_from(
const VerificationType&, ClassVerifier*, bool from_field_is_protected,
TRAPS) const;
+
+ public:
+ static bool resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected,
+ bool from_is_array, bool from_is_object,
+ TRAPS);
};
#endif // SHARE_VM_CLASSFILE_VERIFICATIONTYPE_HPP
diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp
index 58200d11e03..c0976d55d4b 100644
--- a/hotspot/src/share/vm/classfile/verifier.cpp
+++ b/hotspot/src/share/vm/classfile/verifier.cpp
@@ -2377,9 +2377,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_ifnonnull:
target = bcs.dest();
if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) return true;
- // Pop a bytecode starting offset and scan from there.
- bcs.set_start(bci_stack->pop());
+ if (bci_stack->is_empty()) {
+ if (handler_stack->is_empty()) {
+ return true;
+ } else {
+ // Parse the catch handlers for try blocks containing athrow.
+ bcs.set_start(handler_stack->pop());
+ }
+ } else {
+ // Pop a bytecode starting offset and scan from there.
+ bcs.set_start(bci_stack->pop());
+ }
} else {
if (target > bci) { // forward branch
if (target >= code_length) return false;
@@ -2402,9 +2410,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_goto_w:
target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) return true;
- // Been here before, pop new starting offset from stack.
- bcs.set_start(bci_stack->pop());
+ if (bci_stack->is_empty()) {
+ if (handler_stack->is_empty()) {
+ return true;
+ } else {
+ // Parse the catch handlers for try blocks containing athrow.
+ bcs.set_start(handler_stack->pop());
+ }
+ } else {
+ // Been here before, pop new starting offset from stack.
+ bcs.set_start(bci_stack->pop());
+ }
} else {
if (target >= code_length) return false;
// Continue scanning from the target onward.
diff --git a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp
index de81be328d3..1369a3ef866 100644
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp
@@ -1256,9 +1256,7 @@ bool G1CollectedHeap::do_full_collection(bool explicit_gc,
// set between the last GC or pause and now. We need to clear the
// incremental collection set and then start rebuilding it afresh
// after this full GC.
- abandon_collection_set(collection_set()->inc_head());
- collection_set()->clear_incremental();
- collection_set()->stop_incremental_building();
+ abandon_collection_set(collection_set());
tear_down_region_sets(false /* free_list_only */);
collector_state()->set_gcs_are_young(true);
@@ -1379,7 +1377,6 @@ bool G1CollectedHeap::do_full_collection(bool explicit_gc,
_verifier->check_bitmaps("Full GC End");
// Start a new incremental collection set for the next pause
- assert(collection_set()->head() == NULL, "must be");
collection_set()->start_incremental_building();
clear_cset_fast_test();
@@ -1724,8 +1721,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
_old_marking_cycles_started(0),
_old_marking_cycles_completed(0),
_in_cset_fast_test(),
- _worker_cset_start_region(NULL),
- _worker_cset_start_region_time_stamp(NULL),
_gc_timer_stw(new (ResourceObj::C_HEAP, mtGC) STWGCTimer()),
_gc_tracer_stw(new (ResourceObj::C_HEAP, mtGC) G1NewTracer()) {
@@ -1748,8 +1743,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
uint n_queues = ParallelGCThreads;
_task_queues = new RefToScanQueueSet(n_queues);
- _worker_cset_start_region = NEW_C_HEAP_ARRAY(HeapRegion*, n_queues, mtGC);
- _worker_cset_start_region_time_stamp = NEW_C_HEAP_ARRAY(uint, n_queues, mtGC);
_evacuation_failed_info_array = NEW_C_HEAP_ARRAY(EvacuationFailedInfo, n_queues, mtGC);
for (uint i = 0; i < n_queues; i++) {
@@ -1758,7 +1751,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
_task_queues->register_queue(i, q);
::new (&_evacuation_failed_info_array[i]) EvacuationFailedInfo();
}
- clear_cset_start_regions();
// Initialize the G1EvacuationFailureALot counters and flags.
NOT_PRODUCT(reset_evacuation_should_fail();)
@@ -1987,6 +1979,8 @@ jint G1CollectedHeap::initialize() {
_preserved_marks_set.init(ParallelGCThreads);
+ _collection_set.initialize(max_regions());
+
return JNI_OK;
}
@@ -2420,117 +2414,12 @@ G1CollectedHeap::heap_region_par_iterate(HeapRegionClosure* cl,
_hrm.par_iterate(cl, worker_id, hrclaimer, concurrent);
}
-// Clear the cached CSet starting regions and (more importantly)
-// the time stamps. Called when we reset the GC time stamp.
-void G1CollectedHeap::clear_cset_start_regions() {
- assert(_worker_cset_start_region != NULL, "sanity");
- assert(_worker_cset_start_region_time_stamp != NULL, "sanity");
-
- for (uint i = 0; i < ParallelGCThreads; i++) {
- _worker_cset_start_region[i] = NULL;
- _worker_cset_start_region_time_stamp[i] = 0;
- }
-}
-
-// Given the id of a worker, obtain or calculate a suitable
-// starting region for iterating over the current collection set.
-HeapRegion* G1CollectedHeap::start_cset_region_for_worker(uint worker_i) {
- assert(get_gc_time_stamp() > 0, "should have been updated by now");
-
- HeapRegion* result = NULL;
- unsigned gc_time_stamp = get_gc_time_stamp();
-
- if (_worker_cset_start_region_time_stamp[worker_i] == gc_time_stamp) {
- // Cached starting region for current worker was set
- // during the current pause - so it's valid.
- // Note: the cached starting heap region may be NULL
- // (when the collection set is empty).
- result = _worker_cset_start_region[worker_i];
- assert(result == NULL || result->in_collection_set(), "sanity");
- return result;
- }
-
- // The cached entry was not valid so let's calculate
- // a suitable starting heap region for this worker.
-
- // We want the parallel threads to start their collection
- // set iteration at different collection set regions to
- // avoid contention.
- // If we have:
- // n collection set regions
- // p threads
- // Then thread t will start at region floor ((t * n) / p)
-
- result = collection_set()->head();
- uint cs_size = collection_set()->region_length();
- uint active_workers = workers()->active_workers();
-
- uint end_ind = (cs_size * worker_i) / active_workers;
- uint start_ind = 0;
-
- if (worker_i > 0 &&
- _worker_cset_start_region_time_stamp[worker_i - 1] == gc_time_stamp) {
- // Previous workers starting region is valid
- // so let's iterate from there
- start_ind = (cs_size * (worker_i - 1)) / active_workers;
- OrderAccess::loadload();
- result = _worker_cset_start_region[worker_i - 1];
- }
-
- for (uint i = start_ind; i < end_ind; i++) {
- result = result->next_in_collection_set();
- }
-
- // Note: the calculated starting heap region may be NULL
- // (when the collection set is empty).
- assert(result == NULL || result->in_collection_set(), "sanity");
- assert(_worker_cset_start_region_time_stamp[worker_i] != gc_time_stamp,
- "should be updated only once per pause");
- _worker_cset_start_region[worker_i] = result;
- OrderAccess::storestore();
- _worker_cset_start_region_time_stamp[worker_i] = gc_time_stamp;
- return result;
-}
-
void G1CollectedHeap::collection_set_iterate(HeapRegionClosure* cl) {
- HeapRegion* r = collection_set()->head();
- while (r != NULL) {
- HeapRegion* next = r->next_in_collection_set();
- if (cl->doHeapRegion(r)) {
- cl->incomplete();
- return;
- }
- r = next;
- }
+ _collection_set.iterate(cl);
}
-void G1CollectedHeap::collection_set_iterate_from(HeapRegion* r,
- HeapRegionClosure *cl) {
- if (r == NULL) {
- // The CSet is empty so there's nothing to do.
- return;
- }
-
- assert(r->in_collection_set(),
- "Start region must be a member of the collection set.");
- HeapRegion* cur = r;
- while (cur != NULL) {
- HeapRegion* next = cur->next_in_collection_set();
- if (cl->doHeapRegion(cur) && false) {
- cl->incomplete();
- return;
- }
- cur = next;
- }
- cur = collection_set()->head();
- while (cur != r) {
- HeapRegion* next = cur->next_in_collection_set();
- if (cl->doHeapRegion(cur) && false) {
- cl->incomplete();
- return;
- }
- cur = next;
- }
+void G1CollectedHeap::collection_set_iterate_from(HeapRegionClosure *cl, uint worker_id) {
+ _collection_set.iterate_from(cl, worker_id, workers()->active_workers());
}
HeapRegion* G1CollectedHeap::next_compaction_region(const HeapRegion* from) const {
@@ -3090,6 +2979,18 @@ void G1CollectedHeap::wait_for_root_region_scanning() {
g1_policy()->phase_times()->record_root_region_scan_wait_time(wait_time_ms);
}
+class G1PrintCollectionSetClosure : public HeapRegionClosure {
+private:
+ G1HRPrinter* _hr_printer;
+public:
+ G1PrintCollectionSetClosure(G1HRPrinter* hr_printer) : HeapRegionClosure(), _hr_printer(hr_printer) { }
+
+ virtual bool doHeapRegion(HeapRegion* r) {
+ _hr_printer->cset(r);
+ return false;
+ }
+};
+
bool
G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
assert_at_safepoint(true /* should_be_vm_thread */);
@@ -3268,11 +3169,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
_cm->verify_no_cset_oops();
if (_hr_printer.is_active()) {
- HeapRegion* hr = collection_set()->head();
- while (hr != NULL) {
- _hr_printer.cset(hr);
- hr = hr->next_in_collection_set();
- }
+ G1PrintCollectionSetClosure cl(&_hr_printer);
+ _collection_set.iterate(&cl);
}
// Initialize the GC alloc regions.
@@ -3287,12 +3185,10 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
post_evacuate_collection_set(evacuation_info, &per_thread_states);
const size_t* surviving_young_words = per_thread_states.surviving_young_words();
- free_collection_set(collection_set()->head(), evacuation_info, surviving_young_words);
+ free_collection_set(&_collection_set, evacuation_info, surviving_young_words);
eagerly_reclaim_humongous_regions();
- collection_set()->clear_head();
-
record_obj_copy_mem_stats();
_survivor_evac_stats.adjust_desired_plab_sz();
_old_evac_stats.adjust_desired_plab_sz();
@@ -4704,120 +4600,139 @@ void G1CollectedHeap::scrub_rem_set() {
workers()->run_task(&g1_par_scrub_rs_task);
}
-void G1CollectedHeap::free_collection_set(HeapRegion* cs_head, EvacuationInfo& evacuation_info, const size_t* surviving_young_words) {
- size_t pre_used = 0;
- FreeRegionList local_free_list("Local List for CSet Freeing");
+class G1FreeCollectionSetClosure : public HeapRegionClosure {
+private:
+ const size_t* _surviving_young_words;
- double young_time_ms = 0.0;
- double non_young_time_ms = 0.0;
+ FreeRegionList _local_free_list;
+ size_t _rs_lengths;
+ // Bytes used in successfully evacuated regions before the evacuation.
+ size_t _before_used_bytes;
+ // Bytes used in unsucessfully evacuated regions before the evacuation
+ size_t _after_used_bytes;
- _eden.clear();
+ size_t _bytes_allocated_in_old_since_last_gc;
- G1Policy* policy = g1_policy();
+ size_t _failure_used_words;
+ size_t _failure_waste_words;
- double start_sec = os::elapsedTime();
- bool non_young = true;
+ double _young_time;
+ double _non_young_time;
+public:
+ G1FreeCollectionSetClosure(const size_t* surviving_young_words) :
+ HeapRegionClosure(),
+ _surviving_young_words(surviving_young_words),
+ _local_free_list("Local Region List for CSet Freeing"),
+ _rs_lengths(0),
+ _before_used_bytes(0),
+ _after_used_bytes(0),
+ _bytes_allocated_in_old_since_last_gc(0),
+ _failure_used_words(0),
+ _failure_waste_words(0),
+ _young_time(0.0),
+ _non_young_time(0.0) {
+ }
- HeapRegion* cur = cs_head;
- int age_bound = -1;
- size_t rs_lengths = 0;
+ virtual bool doHeapRegion(HeapRegion* r) {
+ double start_time = os::elapsedTime();
- while (cur != NULL) {
- assert(!is_on_master_free_list(cur), "sanity");
- if (non_young) {
- if (cur->is_young()) {
- double end_sec = os::elapsedTime();
- double elapsed_ms = (end_sec - start_sec) * 1000.0;
- non_young_time_ms += elapsed_ms;
+ bool is_young = r->is_young();
- start_sec = os::elapsedTime();
- non_young = false;
- }
+ G1CollectedHeap* g1h = G1CollectedHeap::heap();
+ assert(!g1h->is_on_master_free_list(r), "sanity");
+
+ _rs_lengths += r->rem_set()->occupied_locked();
+
+ assert(r->in_collection_set(), "Region %u should be in collection set.", r->hrm_index());
+ g1h->clear_in_cset(r);
+
+ if (is_young) {
+ int index = r->young_index_in_cset();
+ assert(index != -1, "Young index in collection set must not be -1 for region %u", r->hrm_index());
+ assert((uint) index < g1h->collection_set()->young_region_length(), "invariant");
+ size_t words_survived = _surviving_young_words[index];
+ r->record_surv_words_in_group(words_survived);
} else {
- if (!cur->is_young()) {
- double end_sec = os::elapsedTime();
- double elapsed_ms = (end_sec - start_sec) * 1000.0;
- young_time_ms += elapsed_ms;
-
- start_sec = os::elapsedTime();
- non_young = true;
- }
+ assert(r->young_index_in_cset() == -1, "Young index for old region %u in collection set must be -1", r->hrm_index());
}
- rs_lengths += cur->rem_set()->occupied_locked();
-
- HeapRegion* next = cur->next_in_collection_set();
- assert(cur->in_collection_set(), "bad CS");
- cur->set_next_in_collection_set(NULL);
- clear_in_cset(cur);
-
- if (cur->is_young()) {
- int index = cur->young_index_in_cset();
- assert(index != -1, "invariant");
- assert((uint) index < collection_set()->young_region_length(), "invariant");
- size_t words_survived = surviving_young_words[index];
- cur->record_surv_words_in_group(words_survived);
-
+ if (!r->evacuation_failed()) {
+ assert(r->not_empty(), "Region %u is an empty region in the collection set.", r->hrm_index());
+ _before_used_bytes += r->used();
+ g1h->free_region(r, &_local_free_list, false /* par */, true /* locked */);
} else {
- int index = cur->young_index_in_cset();
- assert(index == -1, "invariant");
- }
-
- assert( (cur->is_young() && cur->young_index_in_cset() > -1) ||
- (!cur->is_young() && cur->young_index_in_cset() == -1),
- "invariant" );
-
- if (!cur->evacuation_failed()) {
- MemRegion used_mr = cur->used_region();
-
- // And the region is empty.
- assert(!used_mr.is_empty(), "Should not have empty regions in a CS.");
- pre_used += cur->used();
- free_region(cur, &local_free_list, false /* par */, true /* locked */);
- } else {
- cur->uninstall_surv_rate_group();
- if (cur->is_young()) {
- cur->set_young_index_in_cset(-1);
- }
- cur->set_evacuation_failed(false);
+ r->uninstall_surv_rate_group();
+ r->set_young_index_in_cset(-1);
+ r->set_evacuation_failed(false);
// When moving a young gen region to old gen, we "allocate" that whole region
// there. This is in addition to any already evacuated objects. Notify the
// policy about that.
// Old gen regions do not cause an additional allocation: both the objects
// still in the region and the ones already moved are accounted for elsewhere.
- if (cur->is_young()) {
- policy->add_bytes_allocated_in_old_since_last_gc(HeapRegion::GrainBytes);
+ if (is_young) {
+ _bytes_allocated_in_old_since_last_gc += HeapRegion::GrainBytes;
}
// The region is now considered to be old.
- cur->set_old();
+ r->set_old();
// Do some allocation statistics accounting. Regions that failed evacuation
// are always made old, so there is no need to update anything in the young
// gen statistics, but we need to update old gen statistics.
- size_t used_words = cur->marked_bytes() / HeapWordSize;
- _old_evac_stats.add_failure_used_and_waste(used_words, HeapRegion::GrainWords - used_words);
- _old_set.add(cur);
- evacuation_info.increment_collectionset_used_after(cur->used());
+ size_t used_words = r->marked_bytes() / HeapWordSize;
+
+ _failure_used_words += used_words;
+ _failure_waste_words += HeapRegion::GrainWords - used_words;
+
+ g1h->old_set_add(r);
+ _after_used_bytes += r->used();
}
- cur = next;
+
+ if (is_young) {
+ _young_time += os::elapsedTime() - start_time;
+ } else {
+ _non_young_time += os::elapsedTime() - start_time;
+ }
+ return false;
}
- evacuation_info.set_regions_freed(local_free_list.length());
- policy->record_max_rs_lengths(rs_lengths);
+ FreeRegionList* local_free_list() { return &_local_free_list; }
+ size_t rs_lengths() const { return _rs_lengths; }
+ size_t before_used_bytes() const { return _before_used_bytes; }
+ size_t after_used_bytes() const { return _after_used_bytes; }
+
+ size_t bytes_allocated_in_old_since_last_gc() const { return _bytes_allocated_in_old_since_last_gc; }
+
+ size_t failure_used_words() const { return _failure_used_words; }
+ size_t failure_waste_words() const { return _failure_waste_words; }
+
+ double young_time() const { return _young_time; }
+ double non_young_time() const { return _non_young_time; }
+};
+
+void G1CollectedHeap::free_collection_set(G1CollectionSet* collection_set, EvacuationInfo& evacuation_info, const size_t* surviving_young_words) {
+ _eden.clear();
+
+ G1FreeCollectionSetClosure cl(surviving_young_words);
+ collection_set_iterate(&cl);
+
+ evacuation_info.set_regions_freed(cl.local_free_list()->length());
+ evacuation_info.increment_collectionset_used_after(cl.after_used_bytes());
+
+ G1Policy* policy = g1_policy();
+
+ policy->record_max_rs_lengths(cl.rs_lengths());
policy->cset_regions_freed();
- double end_sec = os::elapsedTime();
- double elapsed_ms = (end_sec - start_sec) * 1000.0;
+ prepend_to_freelist(cl.local_free_list());
+ decrement_summary_bytes(cl.before_used_bytes());
- if (non_young) {
- non_young_time_ms += elapsed_ms;
- } else {
- young_time_ms += elapsed_ms;
- }
+ policy->add_bytes_allocated_in_old_since_last_gc(cl.bytes_allocated_in_old_since_last_gc());
- prepend_to_freelist(&local_free_list);
- decrement_summary_bytes(pre_used);
- policy->phase_times()->record_young_free_cset_time_ms(young_time_ms);
- policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms);
+ _old_evac_stats.add_failure_used_and_waste(cl.failure_used_words(), cl.failure_waste_words());
+
+ policy->phase_times()->record_young_free_cset_time_ms(cl.young_time() * 1000.0);
+ policy->phase_times()->record_non_young_free_cset_time_ms(cl.non_young_time() * 1000.0);
+
+ collection_set->clear();
}
class G1FreeHumongousRegionClosure : public HeapRegionClosure {
@@ -4960,25 +4875,22 @@ void G1CollectedHeap::eagerly_reclaim_humongous_regions() {
cl.humongous_free_count());
}
-// This routine is similar to the above but does not record
-// any policy statistics or update free lists; we are abandoning
-// the current incremental collection set in preparation of a
-// full collection. After the full GC we will start to build up
-// the incremental collection set again.
-// This is only called when we're doing a full collection
-// and is immediately followed by the tearing down of the young list.
-
-void G1CollectedHeap::abandon_collection_set(HeapRegion* cs_head) {
- HeapRegion* cur = cs_head;
-
- while (cur != NULL) {
- HeapRegion* next = cur->next_in_collection_set();
- assert(cur->in_collection_set(), "bad CS");
- cur->set_next_in_collection_set(NULL);
- clear_in_cset(cur);
- cur->set_young_index_in_cset(-1);
- cur = next;
+class G1AbandonCollectionSetClosure : public HeapRegionClosure {
+public:
+ virtual bool doHeapRegion(HeapRegion* r) {
+ assert(r->in_collection_set(), "Region %u must have been in collection set", r->hrm_index());
+ G1CollectedHeap::heap()->clear_in_cset(r);
+ r->set_young_index_in_cset(-1);
+ return false;
}
+};
+
+void G1CollectedHeap::abandon_collection_set(G1CollectionSet* collection_set) {
+ G1AbandonCollectionSetClosure cl;
+ collection_set->iterate(&cl);
+
+ collection_set->clear();
+ collection_set->stop_incremental_building();
}
void G1CollectedHeap::set_free_regions_coming() {
diff --git a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp
index 66699651b6d..134f3888ee5 100644
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp
@@ -778,13 +778,13 @@ protected:
// The closure used to refine a single card.
RefineCardTableEntryClosure* _refine_cte_cl;
- // After a collection pause, make the regions in the CS into free
+ // After a collection pause, convert the regions in the collection set into free
// regions.
- void free_collection_set(HeapRegion* cs_head, EvacuationInfo& evacuation_info, const size_t* surviving_young_words);
+ void free_collection_set(G1CollectionSet* collection_set, EvacuationInfo& evacuation_info, const size_t* surviving_young_words);
// Abandon the current collection set without recording policy
// statistics or updating free lists.
- void abandon_collection_set(HeapRegion* cs_head);
+ void abandon_collection_set(G1CollectionSet* collection_set);
// The concurrent marker (and the thread it runs in.)
G1ConcurrentMark* _cm;
@@ -930,16 +930,6 @@ protected:
// discovery.
G1CMIsAliveClosure _is_alive_closure_cm;
- // Cache used by G1CollectedHeap::start_cset_region_for_worker().
- HeapRegion** _worker_cset_start_region;
-
- // Time stamp to validate the regions recorded in the cache
- // used by G1CollectedHeap::start_cset_region_for_worker().
- // The heap region entry for a given worker is valid iff
- // the associated time stamp value matches the current value
- // of G1CollectedHeap::_gc_time_stamp.
- uint* _worker_cset_start_region_time_stamp;
-
volatile bool _free_regions_coming;
public:
@@ -1211,19 +1201,14 @@ public:
HeapRegionClaimer* hrclaimer,
bool concurrent = false) const;
- // Clear the cached cset start regions and (more importantly)
- // the time stamps. Called when we reset the GC time stamp.
- void clear_cset_start_regions();
-
- // Given the id of a worker, obtain or calculate a suitable
- // starting region for iterating over the current collection set.
- HeapRegion* start_cset_region_for_worker(uint worker_i);
-
// Iterate over the regions (if any) in the current collection set.
void collection_set_iterate(HeapRegionClosure* blk);
- // As above but starting from region r
- void collection_set_iterate_from(HeapRegion* r, HeapRegionClosure *blk);
+ // Iterate over the regions (if any) in the current collection set. Starts the
+ // iteration over the entire collection set so that the start regions of a given
+ // worker id over the set active_workers are evenly spread across the set of
+ // collection set regions.
+ void collection_set_iterate_from(HeapRegionClosure *blk, uint worker_id);
HeapRegion* next_compaction_region(const HeapRegion* from) const;
diff --git a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.inline.hpp b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.inline.hpp
index 3a66fad03a5..ef847a80a7a 100644
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.inline.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.inline.hpp
@@ -89,16 +89,13 @@ inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const {
}
inline void G1CollectedHeap::reset_gc_time_stamp() {
+ assert_at_safepoint(true);
_gc_time_stamp = 0;
- OrderAccess::fence();
- // Clear the cached CSet starting regions and time stamps.
- // Their validity is dependent on the GC timestamp.
- clear_cset_start_regions();
}
inline void G1CollectedHeap::increment_gc_time_stamp() {
+ assert_at_safepoint(true);
++_gc_time_stamp;
- OrderAccess::fence();
}
inline void G1CollectedHeap::old_set_add(HeapRegion* hr) {
diff --git a/hotspot/src/share/vm/gc/g1/g1CollectionSet.cpp b/hotspot/src/share/vm/gc/g1/g1CollectionSet.cpp
index 1e41dfd89a7..d6797b306d5 100644
--- a/hotspot/src/share/vm/gc/g1/g1CollectionSet.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1CollectionSet.cpp
@@ -30,6 +30,7 @@
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/g1/heapRegionSet.hpp"
+#include "logging/logStream.hpp"
#include "utilities/debug.hpp"
G1CollectorState* G1CollectionSet::collector_state() {
@@ -55,48 +56,63 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) :
_eden_region_length(0),
_survivor_region_length(0),
_old_region_length(0),
-
- _head(NULL),
_bytes_used_before(0),
_recorded_rs_lengths(0),
+ _collection_set_regions(NULL),
+ _collection_set_cur_length(0),
+ _collection_set_max_length(0),
// Incremental CSet attributes
_inc_build_state(Inactive),
- _inc_head(NULL),
- _inc_tail(NULL),
_inc_bytes_used_before(0),
_inc_recorded_rs_lengths(0),
_inc_recorded_rs_lengths_diffs(0),
_inc_predicted_elapsed_time_ms(0.0),
- _inc_predicted_elapsed_time_ms_diffs(0.0),
- _inc_region_length(0) {}
+ _inc_predicted_elapsed_time_ms_diffs(0.0) {
+}
G1CollectionSet::~G1CollectionSet() {
+ if (_collection_set_regions != NULL) {
+ FREE_C_HEAP_ARRAY(uint, _collection_set_regions);
+ }
delete _cset_chooser;
}
void G1CollectionSet::init_region_lengths(uint eden_cset_region_length,
uint survivor_cset_region_length) {
+ assert_at_safepoint(true);
+
_eden_region_length = eden_cset_region_length;
_survivor_region_length = survivor_cset_region_length;
- assert(young_region_length() == _inc_region_length, "should match %u == %u", young_region_length(), _inc_region_length);
+ assert((size_t) young_region_length() == _collection_set_cur_length,
+ "Young region length %u should match collection set length " SIZE_FORMAT, young_region_length(), _collection_set_cur_length);
_old_region_length = 0;
}
+void G1CollectionSet::initialize(uint max_region_length) {
+ guarantee(_collection_set_regions == NULL, "Must only initialize once.");
+ _collection_set_max_length = max_region_length;
+ _collection_set_regions = NEW_C_HEAP_ARRAY(uint, max_region_length, mtGC);
+}
+
void G1CollectionSet::set_recorded_rs_lengths(size_t rs_lengths) {
_recorded_rs_lengths = rs_lengths;
}
// Add the heap region at the head of the non-incremental collection set
void G1CollectionSet::add_old_region(HeapRegion* hr) {
+ assert_at_safepoint(true);
+
assert(_inc_build_state == Active, "Precondition");
assert(hr->is_old(), "the region should be old");
assert(!hr->in_collection_set(), "should not already be in the CSet");
_g1->register_old_region_with_cset(hr);
- hr->set_next_in_collection_set(_head);
- _head = hr;
+
+ _collection_set_regions[_collection_set_cur_length++] = hr->hrm_index();
+ assert(_collection_set_cur_length <= _collection_set_max_length, "Collection set now larger than maximum size.");
+
_bytes_used_before += hr->used();
size_t rs_length = hr->rem_set()->occupied();
_recorded_rs_lengths += rs_length;
@@ -105,12 +121,10 @@ void G1CollectionSet::add_old_region(HeapRegion* hr) {
// Initialize the per-collection-set information
void G1CollectionSet::start_incremental_building() {
+ assert(_collection_set_cur_length == 0, "Collection set must be empty before starting a new collection set.");
assert(_inc_build_state == Inactive, "Precondition");
- _inc_head = NULL;
- _inc_tail = NULL;
_inc_bytes_used_before = 0;
- _inc_region_length = 0;
_inc_recorded_rs_lengths = 0;
_inc_recorded_rs_lengths_diffs = 0;
@@ -151,6 +165,38 @@ void G1CollectionSet::finalize_incremental_building() {
_inc_predicted_elapsed_time_ms_diffs = 0.0;
}
+void G1CollectionSet::clear() {
+ assert_at_safepoint(true);
+ _collection_set_cur_length = 0;
+}
+
+void G1CollectionSet::iterate(HeapRegionClosure* cl) const {
+ iterate_from(cl, 0, 1);
+}
+
+void G1CollectionSet::iterate_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const {
+ size_t len = _collection_set_cur_length;
+ OrderAccess::loadload();
+ if (len == 0) {
+ return;
+ }
+ size_t start_pos = (worker_id * len) / total_workers;
+ size_t cur_pos = start_pos;
+
+ do {
+ HeapRegion* r = G1CollectedHeap::heap()->region_at(_collection_set_regions[cur_pos]);
+ bool result = cl->doHeapRegion(r);
+ if (result) {
+ cl->incomplete();
+ return;
+ }
+ cur_pos++;
+ if (cur_pos == len) {
+ cur_pos = 0;
+ }
+ } while (cur_pos != start_pos);
+}
+
void G1CollectionSet::update_young_region_prediction(HeapRegion* hr,
size_t new_rs_length) {
// Update the CSet information that is dependent on the new RS length
@@ -183,8 +229,16 @@ void G1CollectionSet::add_young_region_common(HeapRegion* hr) {
assert(hr->is_young(), "invariant");
assert(_inc_build_state == Active, "Precondition");
- hr->set_young_index_in_cset(_inc_region_length);
- _inc_region_length++;
+ size_t collection_set_length = _collection_set_cur_length;
+ assert(collection_set_length <= INT_MAX, "Collection set is too large with %d entries", (int)collection_set_length);
+ hr->set_young_index_in_cset((int)collection_set_length);
+
+ _collection_set_regions[collection_set_length] = hr->hrm_index();
+ // Concurrent readers must observe the store of the value in the array before an
+ // update to the length field.
+ OrderAccess::storestore();
+ _collection_set_cur_length++;
+ assert(_collection_set_cur_length <= _collection_set_max_length, "Collection set larger than maximum allowed.");
// This routine is used when:
// * adding survivor regions to the incremental cset at the end of an
@@ -218,59 +272,81 @@ void G1CollectionSet::add_young_region_common(HeapRegion* hr) {
assert(!hr->in_collection_set(), "invariant");
_g1->register_young_region_with_cset(hr);
- assert(hr->next_in_collection_set() == NULL, "invariant");
}
-// Add the region at the RHS of the incremental cset
void G1CollectionSet::add_survivor_regions(HeapRegion* hr) {
- // We should only ever be appending survivors at the end of a pause
- assert(hr->is_survivor(), "Logic");
-
- // Do the 'common' stuff
+ assert(hr->is_survivor(), "Must only add survivor regions, but is %s", hr->get_type_str());
add_young_region_common(hr);
-
- // Now add the region at the right hand side
- if (_inc_tail == NULL) {
- assert(_inc_head == NULL, "invariant");
- _inc_head = hr;
- } else {
- _inc_tail->set_next_in_collection_set(hr);
- }
- _inc_tail = hr;
}
-// Add the region to the LHS of the incremental cset
void G1CollectionSet::add_eden_region(HeapRegion* hr) {
- // Survivors should be added to the RHS at the end of a pause
- assert(hr->is_eden(), "Logic");
-
- // Do the 'common' stuff
+ assert(hr->is_eden(), "Must only add eden regions, but is %s", hr->get_type_str());
add_young_region_common(hr);
-
- // Add the region at the left hand side
- hr->set_next_in_collection_set(_inc_head);
- if (_inc_head == NULL) {
- assert(_inc_tail == NULL, "Invariant");
- _inc_tail = hr;
- }
- _inc_head = hr;
}
#ifndef PRODUCT
-void G1CollectionSet::print(HeapRegion* list_head, outputStream* st) {
- assert(list_head == inc_head() || list_head == head(), "must be");
+class G1VerifyYoungAgesClosure : public HeapRegionClosure {
+public:
+ bool _valid;
+public:
+ G1VerifyYoungAgesClosure() : HeapRegionClosure(), _valid(true) { }
- st->print_cr("\nCollection_set:");
- HeapRegion* csr = list_head;
- while (csr != NULL) {
- HeapRegion* next = csr->next_in_collection_set();
- assert(csr->in_collection_set(), "bad CS");
- st->print_cr(" " HR_FORMAT ", P: " PTR_FORMAT "N: " PTR_FORMAT ", age: %4d",
- HR_FORMAT_PARAMS(csr),
- p2i(csr->prev_top_at_mark_start()), p2i(csr->next_top_at_mark_start()),
- csr->age_in_surv_rate_group_cond());
- csr = next;
+ virtual bool doHeapRegion(HeapRegion* r) {
+ guarantee(r->is_young(), "Region must be young but is %s", r->get_type_str());
+
+ SurvRateGroup* group = r->surv_rate_group();
+
+ if (group == NULL) {
+ log_error(gc, verify)("## encountered NULL surv_rate_group in young region");
+ _valid = false;
+ }
+
+ if (r->age_in_surv_rate_group() < 0) {
+ log_error(gc, verify)("## encountered negative age in young region");
+ _valid = false;
+ }
+
+ return false;
}
+
+ bool valid() const { return _valid; }
+};
+
+bool G1CollectionSet::verify_young_ages() {
+ assert_at_safepoint(true);
+
+ G1VerifyYoungAgesClosure cl;
+ iterate(&cl);
+
+ if (!cl.valid()) {
+ LogStreamHandle(Error, gc, verify) log;
+ print(&log);
+ }
+
+ return cl.valid();
+}
+
+class G1PrintCollectionSetClosure : public HeapRegionClosure {
+ outputStream* _st;
+public:
+ G1PrintCollectionSetClosure(outputStream* st) : HeapRegionClosure(), _st(st) { }
+
+ virtual bool doHeapRegion(HeapRegion* r) {
+ assert(r->in_collection_set(), "Region %u should be in collection set", r->hrm_index());
+ _st->print_cr(" " HR_FORMAT ", P: " PTR_FORMAT "N: " PTR_FORMAT ", age: %4d",
+ HR_FORMAT_PARAMS(r),
+ p2i(r->prev_top_at_mark_start()),
+ p2i(r->next_top_at_mark_start()),
+ r->age_in_surv_rate_group_cond());
+ return false;
+ }
+};
+
+void G1CollectionSet::print(outputStream* st) {
+ st->print_cr("\nCollection_set:");
+
+ G1PrintCollectionSetClosure cl(st);
+ iterate(&cl);
}
#endif // !PRODUCT
@@ -281,7 +357,6 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi
guarantee(target_pause_time_ms > 0.0,
"target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms);
- guarantee(_head == NULL, "Precondition");
size_t pending_cards = _policy->pending_cards();
double base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards);
@@ -305,7 +380,6 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi
// Clear the fields that point to the survivor list - they are all young now.
survivors->convert_to_eden();
- _head = _inc_head;
_bytes_used_before = _inc_bytes_used_before;
time_remaining_ms = MAX2(time_remaining_ms - _inc_predicted_elapsed_time_ms, 0.0);
@@ -422,23 +496,41 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) {
}
#ifdef ASSERT
+class G1VerifyYoungCSetIndicesClosure : public HeapRegionClosure {
+private:
+ size_t _young_length;
+ int* _heap_region_indices;
+public:
+ G1VerifyYoungCSetIndicesClosure(size_t young_length) : HeapRegionClosure(), _young_length(young_length) {
+ _heap_region_indices = NEW_C_HEAP_ARRAY(int, young_length, mtGC);
+ for (size_t i = 0; i < young_length; i++) {
+ _heap_region_indices[i] = -1;
+ }
+ }
+ ~G1VerifyYoungCSetIndicesClosure() {
+ FREE_C_HEAP_ARRAY(int, _heap_region_indices);
+ }
+
+ virtual bool doHeapRegion(HeapRegion* r) {
+ const int idx = r->young_index_in_cset();
+
+ assert(idx > -1, "Young index must be set for all regions in the incremental collection set but is not for region %u.", r->hrm_index());
+ assert((size_t)idx < _young_length, "Young cset index too large for region %u", r->hrm_index());
+
+ assert(_heap_region_indices[idx] == -1,
+ "Index %d used by multiple regions, first use by region %u, second by region %u",
+ idx, _heap_region_indices[idx], r->hrm_index());
+
+ _heap_region_indices[idx] = r->hrm_index();
+
+ return false;
+ }
+};
+
void G1CollectionSet::verify_young_cset_indices() const {
- ResourceMark rm;
- uint* heap_region_indices = NEW_RESOURCE_ARRAY(uint, young_region_length());
- for (uint i = 0; i < young_region_length(); ++i) {
- heap_region_indices[i] = (uint)-1;
- }
+ assert_at_safepoint(true);
- for (HeapRegion* hr = _inc_head; hr != NULL; hr = hr->next_in_collection_set()) {
- const int idx = hr->young_index_in_cset();
- assert(idx > -1, "must be set for all inc cset regions");
- assert((uint)idx < young_region_length(), "young cset index too large");
-
- assert(heap_region_indices[idx] == (uint)-1,
- "index %d used by multiple regions, first use by %u, second by %u",
- idx, heap_region_indices[idx], hr->hrm_index());
-
- heap_region_indices[idx] = hr->hrm_index();
- }
+ G1VerifyYoungCSetIndicesClosure cl(_collection_set_cur_length);
+ iterate(&cl);
}
#endif
diff --git a/hotspot/src/share/vm/gc/g1/g1CollectionSet.hpp b/hotspot/src/share/vm/gc/g1/g1CollectionSet.hpp
index bd6f41f7b95..24c62316124 100644
--- a/hotspot/src/share/vm/gc/g1/g1CollectionSet.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1CollectionSet.hpp
@@ -47,10 +47,15 @@ class G1CollectionSet VALUE_OBJ_CLASS_SPEC {
uint _survivor_region_length;
uint _old_region_length;
- // The head of the list (via "next_in_collection_set()") representing the
- // current collection set. Set from the incrementally built collection
- // set at the start of the pause.
- HeapRegion* _head;
+ // The actual collection set as a set of region indices.
+ // All entries in _collection_set_regions below _collection_set_cur_length are
+ // assumed to be valid entries.
+ // We assume that at any time there is at most only one writer and (one or more)
+ // concurrent readers. This means we are good with using storestore and loadload
+ // barriers on the writer and reader respectively only.
+ uint* _collection_set_regions;
+ volatile size_t _collection_set_cur_length;
+ size_t _collection_set_max_length;
// The number of bytes in the collection set before the pause. Set from
// the incrementally built collection set at the start of an evacuation
@@ -71,12 +76,6 @@ class G1CollectionSet VALUE_OBJ_CLASS_SPEC {
CSetBuildType _inc_build_state;
- // The head of the incrementally built collection set.
- HeapRegion* _inc_head;
-
- // The tail of the incrementally built collection set.
- HeapRegion* _inc_tail;
-
// The number of bytes in the incrementally built collection set.
// Used to set _collection_set_bytes_used_before at the start of
// an evacuation pause.
@@ -105,8 +104,6 @@ class G1CollectionSet VALUE_OBJ_CLASS_SPEC {
// See the comment for _inc_recorded_rs_lengths_diffs.
double _inc_predicted_elapsed_time_ms_diffs;
- uint _inc_region_length;
-
G1CollectorState* collector_state();
G1GCPhaseTimes* phase_times();
@@ -117,6 +114,9 @@ public:
G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy);
~G1CollectionSet();
+ // Initializes the collection set giving the maximum possible length of the collection set.
+ void initialize(uint max_region_length);
+
CollectionSetChooser* cset_chooser();
void init_region_lengths(uint eden_cset_region_length,
@@ -133,36 +133,31 @@ public:
uint survivor_region_length() const { return _survivor_region_length; }
uint old_region_length() const { return _old_region_length; }
- // Incremental CSet Support
-
- // The head of the incrementally built collection set.
- HeapRegion* inc_head() { return _inc_head; }
-
- // The tail of the incrementally built collection set.
- HeapRegion* inc_tail() { return _inc_tail; }
+ // Incremental collection set support
// Initialize incremental collection set info.
void start_incremental_building();
- // Perform any final calculations on the incremental CSet fields
+ // Perform any final calculations on the incremental collection set fields
// before we can use them.
void finalize_incremental_building();
- void clear_incremental() {
- _inc_head = NULL;
- _inc_tail = NULL;
- _inc_region_length = 0;
- }
+ // Reset the contents of the collection set.
+ void clear();
- // Stop adding regions to the incremental collection set
+ // Iterate over the collection set, applying the given HeapRegionClosure on all of them.
+ // If may_be_aborted is true, iteration may be aborted using the return value of the
+ // called closure method.
+ void iterate(HeapRegionClosure* cl) const;
+
+ // Iterate over the collection set, applying the given HeapRegionClosure on all of them,
+ // trying to optimally spread out starting position of total_workers workers given the
+ // caller's worker_id.
+ void iterate_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const;
+
+ // Stop adding regions to the incremental collection set.
void stop_incremental_building() { _inc_build_state = Inactive; }
- // The head of the list (via "next_in_collection_set()") representing the
- // current collection set.
- HeapRegion* head() { return _head; }
-
- void clear_head() { _head = NULL; }
-
size_t recorded_rs_lengths() { return _recorded_rs_lengths; }
size_t bytes_used_before() const {
@@ -174,33 +169,32 @@ public:
}
// Choose a new collection set. Marks the chosen regions as being
- // "in_collection_set", and links them together. The head and number of
- // the collection set are available via access methods.
+ // "in_collection_set".
double finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors);
void finalize_old_part(double time_remaining_ms);
- // Add old region "hr" to the CSet.
+ // Add old region "hr" to the collection set.
void add_old_region(HeapRegion* hr);
// Update information about hr in the aggregated information for
// the incrementally built collection set.
void update_young_region_prediction(HeapRegion* hr, size_t new_rs_length);
- // Add hr to the LHS of the incremental collection set.
+ // Add eden region to the collection set.
void add_eden_region(HeapRegion* hr);
- // Add hr to the RHS of the incremental collection set.
+ // Add survivor region to the collection set.
void add_survivor_regions(HeapRegion* hr);
#ifndef PRODUCT
- void print(HeapRegion* list_head, outputStream* st);
+ bool verify_young_ages();
+
+ void print(outputStream* st);
#endif // !PRODUCT
private:
- // Update the incremental cset information when adding a region
- // (should not be called directly).
+ // Update the incremental collection set information when adding a region.
void add_young_region_common(HeapRegion* hr);
-
};
#endif // SHARE_VM_GC_G1_G1COLLECTIONSET_HPP
diff --git a/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.cpp b/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.cpp
index 37d72134cfd..c15a3823f8a 100644
--- a/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.cpp
@@ -394,37 +394,6 @@ void G1DefaultPolicy::update_rs_lengths_prediction(size_t prediction) {
}
}
-#ifndef PRODUCT
-bool G1DefaultPolicy::verify_young_ages() {
- bool ret = true;
-
- for (HeapRegion* curr = _collection_set->inc_head();
- curr != NULL;
- curr = curr->next_in_collection_set()) {
- guarantee(curr->is_young(), "Region must be young");
-
- SurvRateGroup* group = curr->surv_rate_group();
-
- if (group == NULL) {
- log_error(gc, verify)("## encountered NULL surv_rate_group in young region");
- ret = false;
- }
-
- if (curr->age_in_surv_rate_group() < 0) {
- log_error(gc, verify)("## encountered negative age in young region");
- ret = false;
- }
- }
-
- if (!ret) {
- LogStreamHandle(Error, gc, verify) log;
- _collection_set->print(_collection_set->inc_head(), &log);
- }
-
- return ret;
-}
-#endif // PRODUCT
-
void G1DefaultPolicy::record_full_collection_start() {
_full_collection_start_sec = os::elapsedTime();
// Release the future to-space so that it is available for compaction into.
@@ -488,7 +457,7 @@ void G1DefaultPolicy::record_collection_pause_start(double start_time_sec) {
_short_lived_surv_rate_group->stop_adding_regions();
_survivors_age_table.clear();
- assert( verify_young_ages(), "region age verification" );
+ assert(_g1->collection_set()->verify_young_ages(), "region age verification failed");
}
void G1DefaultPolicy::record_concurrent_mark_init_end(double mark_init_elapsed_time_ms) {
diff --git a/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.hpp b/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.hpp
index 263da38203e..21ab55a4ab4 100644
--- a/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1DefaultPolicy.hpp
@@ -89,10 +89,6 @@ class G1DefaultPolicy: public G1Policy {
size_t _rs_lengths_prediction;
-#ifndef PRODUCT
- bool verify_young_ages(HeapRegion* head, SurvRateGroup *surv_rate_group);
-#endif // PRODUCT
-
size_t _pending_cards;
// The amount of allocated bytes in old gen during the last mutator and the following
@@ -116,10 +112,6 @@ public:
hr->install_surv_rate_group(_survivor_surv_rate_group);
}
-#ifndef PRODUCT
- bool verify_young_ages();
-#endif // PRODUCT
-
void record_max_rs_lengths(size_t rs_lengths) {
_max_rs_lengths = rs_lengths;
}
diff --git a/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp b/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp
index f6c7371de6e..7ff07bc5a72 100644
--- a/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp
@@ -251,6 +251,5 @@ G1ParRemoveSelfForwardPtrsTask::G1ParRemoveSelfForwardPtrsTask() :
void G1ParRemoveSelfForwardPtrsTask::work(uint worker_id) {
RemoveSelfForwardPtrHRClosure rsfp_cl(worker_id, &_hrclaimer);
- HeapRegion* hr = _g1h->start_cset_region_for_worker(worker_id);
- _g1h->collection_set_iterate_from(hr, &rsfp_cl);
+ _g1h->collection_set_iterate_from(&rsfp_cl, worker_id);
}
diff --git a/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp b/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp
index b8093e4fb94..c3970d71991 100644
--- a/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp
@@ -580,15 +580,20 @@ void G1HeapVerifier::verify_dirty_region(HeapRegion* hr) {
}
}
-void G1HeapVerifier::verify_dirty_young_list(HeapRegion* head) {
- G1SATBCardTableModRefBS* ct_bs = _g1h->g1_barrier_set();
- for (HeapRegion* hr = head; hr != NULL; hr = hr->next_in_collection_set()) {
- verify_dirty_region(hr);
+class G1VerifyDirtyYoungListClosure : public HeapRegionClosure {
+private:
+ G1HeapVerifier* _verifier;
+public:
+ G1VerifyDirtyYoungListClosure(G1HeapVerifier* verifier) : HeapRegionClosure(), _verifier(verifier) { }
+ virtual bool doHeapRegion(HeapRegion* r) {
+ _verifier->verify_dirty_region(r);
+ return false;
}
-}
+};
void G1HeapVerifier::verify_dirty_young_regions() {
- verify_dirty_young_list(_g1h->collection_set()->inc_head());
+ G1VerifyDirtyYoungListClosure cl(this);
+ _g1h->collection_set()->iterate(&cl);
}
bool G1HeapVerifier::verify_no_bits_over_tams(const char* bitmap_name, G1CMBitMapRO* bitmap,
diff --git a/hotspot/src/share/vm/gc/g1/g1HeapVerifier.hpp b/hotspot/src/share/vm/gc/g1/g1HeapVerifier.hpp
index 4d6aa684093..70f4ea32d4d 100644
--- a/hotspot/src/share/vm/gc/g1/g1HeapVerifier.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1HeapVerifier.hpp
@@ -108,7 +108,6 @@ public:
void verify_not_dirty_region(HeapRegion* hr) PRODUCT_RETURN;
void verify_dirty_region(HeapRegion* hr) PRODUCT_RETURN;
- void verify_dirty_young_list(HeapRegion* head) PRODUCT_RETURN;
void verify_dirty_young_regions() PRODUCT_RETURN;
};
diff --git a/hotspot/src/share/vm/gc/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc/g1/g1RemSet.cpp
index 9871a7ce962..4e4bb9904cf 100644
--- a/hotspot/src/share/vm/gc/g1/g1RemSet.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1RemSet.cpp
@@ -382,10 +382,8 @@ size_t G1RemSet::scan_rem_set(G1ParPushHeapRSClosure* oops_in_heap_closure,
uint worker_i) {
double rs_time_start = os::elapsedTime();
- HeapRegion *startRegion = _g1->start_cset_region_for_worker(worker_i);
-
G1ScanRSClosure cl(_scan_state, oops_in_heap_closure, heap_region_codeblobs, worker_i);
- _g1->collection_set_iterate_from(startRegion, &cl);
+ _g1->collection_set_iterate_from(&cl, worker_i);
double scan_rs_time_sec = (os::elapsedTime() - rs_time_start) -
cl.strong_code_root_scan_time_sec();
diff --git a/hotspot/src/share/vm/gc/g1/g1StringDedupQueue.cpp b/hotspot/src/share/vm/gc/g1/g1StringDedupQueue.cpp
index 3e092fe8c38..3bca86a80a5 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupQueue.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupQueue.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -154,8 +154,8 @@ void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
}
void G1StringDedupQueue::print_statistics() {
- log_debug(gc, stringdedup)(" [Queue]");
- log_debug(gc, stringdedup)(" [Dropped: " UINTX_FORMAT "]", _queue->_dropped);
+ log_debug(gc, stringdedup)(" Queue");
+ log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _queue->_dropped);
}
void G1StringDedupQueue::verify() {
diff --git a/hotspot/src/share/vm/gc/g1/g1StringDedupStat.cpp b/hotspot/src/share/vm/gc/g1/g1StringDedupStat.cpp
index 4c3349acae5..6443f6e8a92 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupStat.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupStat.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,9 @@ G1StringDedupStat::G1StringDedupStat() :
_idle(0),
_exec(0),
_block(0),
- _start(0.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) {
@@ -69,7 +71,13 @@ void G1StringDedupStat::add(const G1StringDedupStat& stat) {
_block_elapsed += stat._block_elapsed;
}
-void G1StringDedupStat::print_summary(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
+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) {
@@ -79,13 +87,16 @@ void G1StringDedupStat::print_summary(const G1StringDedupStat& last_stat, const
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_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,
- last_stat._exec_elapsed);
+ 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) {
@@ -134,23 +145,31 @@ void G1StringDedupStat::print_statistics(const G1StringDedupStat& stat, bool tot
if (total) {
log_debug(gc, stringdedup)(
- " [Total Exec: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT ", Idle: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT "]",
- stat._exec, stat._exec_elapsed, stat._idle, stat._idle_elapsed, stat._block, stat._block_elapsed);
+ " 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));
} else {
log_debug(gc, stringdedup)(
- " [Last Exec: " G1_STRDEDUP_TIME_FORMAT ", Idle: " G1_STRDEDUP_TIME_FORMAT ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT "]",
- stat._exec_elapsed, stat._idle_elapsed, stat._block, stat._block_elapsed);
+ " 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));
}
- 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 "]",
+ 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 ")]",
+ 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 ")]",
+ 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 ")]",
+ 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);
}
diff --git a/hotspot/src/share/vm/gc/g1/g1StringDedupStat.hpp b/hotspot/src/share/vm/gc/g1/g1StringDedupStat.hpp
index ff0dbb51ad7..d83e627fdc4 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupStat.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupStat.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,11 +30,14 @@
// Macros for GC log output formating
#define G1_STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12)
-#define G1_STRDEDUP_TIME_FORMAT "%1.7lf secs"
-#define G1_STRDEDUP_PERCENT_FORMAT "%5.1lf%%"
-#define G1_STRDEDUP_PERCENT_FORMAT_NS "%.1lf%%"
-#define G1_STRDEDUP_BYTES_FORMAT "%8.1lf%s"
-#define G1_STRDEDUP_BYTES_FORMAT_NS "%.1lf%s"
+#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))
//
@@ -60,7 +63,9 @@ private:
uintx _block;
// Time spent by the deduplication thread in different phases
- double _start;
+ double _start_concurrent;
+ double _end_concurrent;
+ double _start_phase;
double _idle_elapsed;
double _exec_elapsed;
double _block_elapsed;
@@ -104,38 +109,41 @@ public:
}
void mark_idle() {
- _start = os::elapsedTime();
+ _start_phase = os::elapsedTime();
_idle++;
}
void mark_exec() {
double now = os::elapsedTime();
- _idle_elapsed = now - _start;
- _start = now;
+ _idle_elapsed = now - _start_phase;
+ _start_phase = now;
+ _start_concurrent = now;
_exec++;
}
void mark_block() {
double now = os::elapsedTime();
- _exec_elapsed += now - _start;
- _start = now;
+ _exec_elapsed += now - _start_phase;
+ _start_phase = now;
_block++;
}
void mark_unblock() {
double now = os::elapsedTime();
- _block_elapsed += now - _start;
- _start = now;
+ _block_elapsed += now - _start_phase;
+ _start_phase = now;
}
void mark_done() {
double now = os::elapsedTime();
- _exec_elapsed += now - _start;
+ _exec_elapsed += now - _start_phase;
+ _end_concurrent = now;
}
void add(const G1StringDedupStat& stat);
- static void print_summary(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_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);
};
diff --git a/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp b/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp
index 01ef9dcc3c2..1554ef458ba 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp
@@ -196,7 +196,8 @@ void G1StringDedupEntryCache::delete_overflowed() {
}
double end = os::elapsedTime();
- log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT, count, end - start);
+ log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT_MS,
+ count, G1_STRDEDUP_TIME_PARAM_MS(end - start));
}
G1StringDedupTable* G1StringDedupTable::_table = NULL;
@@ -610,14 +611,14 @@ void G1StringDedupTable::clean_entry_cache() {
void G1StringDedupTable::print_statistics() {
Log(gc, stringdedup) log;
- log.debug(" [Table]");
- log.debug(" [Memory Usage: " G1_STRDEDUP_BYTES_FORMAT_NS "]",
+ 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(" [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(" 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,
_table->_entries, (double)_table->_entries / (double)_table->_size * 100.0, _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 "(" G1_STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" G1_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);
+ 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/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp
index 57337d1d7dd..0bdfc193064 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp
@@ -103,6 +103,7 @@ void G1StringDedupThread::run_service() {
SuspendibleThreadSetJoiner sts_join;
stat.mark_exec();
+ print_start(stat);
// Process the queue
for (;;) {
@@ -123,9 +124,8 @@ void G1StringDedupThread::run_service() {
stat.mark_done();
- // Print statistics
total_stat.add(stat);
- print(stat, total_stat);
+ print_end(stat, total_stat);
}
G1StringDedupTable::clean_entry_cache();
@@ -136,14 +136,16 @@ void G1StringDedupThread::stop_service() {
G1StringDedupQueue::cancel_wait();
}
-void G1StringDedupThread::print(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
- if (log_is_enabled(Info, gc, stringdedup)) {
- G1StringDedupStat::print_summary(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();
- }
+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/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp
index ff568114d86..651112519c4 100644
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp
@@ -43,7 +43,8 @@ private:
G1StringDedupThread();
~G1StringDedupThread();
- void print(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
+ void print_start(const G1StringDedupStat& last_stat);
+ void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
void run_service();
void stop_service();
diff --git a/hotspot/src/share/vm/gc/g1/g1YoungRemSetSamplingThread.cpp b/hotspot/src/share/vm/gc/g1/g1YoungRemSetSamplingThread.cpp
index 5e57885d8ed..d94dbd868e3 100644
--- a/hotspot/src/share/vm/gc/g1/g1YoungRemSetSamplingThread.cpp
+++ b/hotspot/src/share/vm/gc/g1/g1YoungRemSetSamplingThread.cpp
@@ -71,38 +71,51 @@ void G1YoungRemSetSamplingThread::stop_service() {
_monitor.notify();
}
+class G1YoungRemSetSamplingClosure : public HeapRegionClosure {
+ SuspendibleThreadSetJoiner* _sts;
+ size_t _regions_visited;
+ size_t _sampled_rs_lengths;
+public:
+ G1YoungRemSetSamplingClosure(SuspendibleThreadSetJoiner* sts) :
+ HeapRegionClosure(), _sts(sts), _regions_visited(0), _sampled_rs_lengths(0) { }
+
+ virtual bool doHeapRegion(HeapRegion* r) {
+ size_t rs_length = r->rem_set()->occupied();
+ _sampled_rs_lengths += rs_length;
+
+ // Update the collection set policy information for this region
+ G1CollectedHeap::heap()->collection_set()->update_young_region_prediction(r, rs_length);
+
+ _regions_visited++;
+
+ if (_regions_visited == 10) {
+ if (_sts->should_yield()) {
+ _sts->yield();
+ // A gc may have occurred and our sampling data is stale and further
+ // traversal of the collection set is unsafe
+ return true;
+ }
+ _regions_visited = 0;
+ }
+ return false;
+ }
+
+ size_t sampled_rs_lengths() const { return _sampled_rs_lengths; }
+};
+
void G1YoungRemSetSamplingThread::sample_young_list_rs_lengths() {
SuspendibleThreadSetJoiner sts;
G1CollectedHeap* g1h = G1CollectedHeap::heap();
G1Policy* g1p = g1h->g1_policy();
- G1CollectionSet* g1cs = g1h->collection_set();
+
if (g1p->adaptive_young_list_length()) {
- int regions_visited = 0;
- HeapRegion* hr = g1cs->inc_head();
- size_t sampled_rs_lengths = 0;
+ G1YoungRemSetSamplingClosure cl(&sts);
- while (hr != NULL) {
- size_t rs_length = hr->rem_set()->occupied();
- sampled_rs_lengths += rs_length;
+ G1CollectionSet* g1cs = g1h->collection_set();
+ g1cs->iterate(&cl);
- // Update the collection set policy information for this region
- g1cs->update_young_region_prediction(hr, rs_length);
-
- ++regions_visited;
-
- // we try to yield every time we visit 10 regions
- if (regions_visited == 10) {
- if (sts.should_yield()) {
- sts.yield();
- // A gc may have occurred and our sampling data is stale and further
- // traversal of the collection set is unsafe
- return;
- }
- regions_visited = 0;
- }
- assert(hr == g1cs->inc_tail() || hr->next_in_collection_set() != NULL, "next should only be null at tail of icset");
- hr = hr->next_in_collection_set();
+ if (cl.complete()) {
+ g1p->revise_young_list_target_length_if_necessary(cl.sampled_rs_lengths());
}
- g1p->revise_young_list_target_length_if_necessary(sampled_rs_lengths);
}
}
diff --git a/hotspot/src/share/vm/gc/g1/heapRegion.cpp b/hotspot/src/share/vm/gc/g1/heapRegion.cpp
index 44ef49423b4..52ad443ea67 100644
--- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp
@@ -284,7 +284,6 @@ HeapRegion::HeapRegion(uint hrm_index,
_hrm_index(hrm_index),
_allocation_context(AllocationContext::system()),
_humongous_start_region(NULL),
- _next_in_special_set(NULL),
_evacuation_failed(false),
_prev_marked_bytes(0), _next_marked_bytes(0), _gc_efficiency(0.0),
_next(NULL), _prev(NULL),
diff --git a/hotspot/src/share/vm/gc/g1/heapRegion.hpp b/hotspot/src/share/vm/gc/g1/heapRegion.hpp
index 10d1cc59d3e..a020792d6fa 100644
--- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp
@@ -261,12 +261,6 @@ class HeapRegion: public G1ContiguousSpace {
// True iff an attempt to evacuate an object in the region failed.
bool _evacuation_failed;
- // A heap region may be a member one of a number of special subsets, each
- // represented as linked lists through the field below. Currently, there
- // is only one set:
- // The collection set.
- HeapRegion* _next_in_special_set;
-
// Fields used by the HeapRegionSetBase class and subclasses.
HeapRegion* _next;
HeapRegion* _prev;
@@ -476,9 +470,6 @@ class HeapRegion: public G1ContiguousSpace {
inline bool in_collection_set() const;
- inline HeapRegion* next_in_collection_set() const;
- inline void set_next_in_collection_set(HeapRegion* r);
-
void set_allocation_context(AllocationContext_t context) {
_allocation_context = context;
}
@@ -744,7 +735,7 @@ class HeapRegion: public G1ContiguousSpace {
// Terminates the iteration when the "doHeapRegion" method returns "true".
class HeapRegionClosure : public StackObj {
friend class HeapRegionManager;
- friend class G1CollectedHeap;
+ friend class G1CollectionSet;
bool _complete;
void incomplete() { _complete = false; }
diff --git a/hotspot/src/share/vm/gc/g1/heapRegion.inline.hpp b/hotspot/src/share/vm/gc/g1/heapRegion.inline.hpp
index 2138155fb9d..248e0befa1d 100644
--- a/hotspot/src/share/vm/gc/g1/heapRegion.inline.hpp
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -230,18 +230,4 @@ inline bool HeapRegion::in_collection_set() const {
return G1CollectedHeap::heap()->is_in_cset(this);
}
-inline HeapRegion* HeapRegion::next_in_collection_set() const {
- assert(in_collection_set(), "should only invoke on member of CS.");
- assert(_next_in_special_set == NULL ||
- _next_in_special_set->in_collection_set(),
- "Malformed CS.");
- return _next_in_special_set;
-}
-
-void HeapRegion::set_next_in_collection_set(HeapRegion* r) {
- assert(in_collection_set(), "should only invoke on member of CS.");
- assert(r == NULL || r->in_collection_set(), "Malformed CS.");
- _next_in_special_set = r;
-}
-
#endif // SHARE_VM_GC_G1_HEAPREGION_INLINE_HPP
diff --git a/hotspot/src/share/vm/gc/parallel/gcTaskManager.cpp b/hotspot/src/share/vm/gc/parallel/gcTaskManager.cpp
index 2ec5d190293..4b47eef70c8 100644
--- a/hotspot/src/share/vm/gc/parallel/gcTaskManager.cpp
+++ b/hotspot/src/share/vm/gc/parallel/gcTaskManager.cpp
@@ -386,13 +386,21 @@ GCTaskThread* GCTaskManager::install_worker(uint t) {
void GCTaskManager::add_workers(bool initializing) {
os::ThreadType worker_type = os::pgc_thread;
+ uint previous_created_workers = _created_workers;
+
_created_workers = WorkerManager::add_workers(this,
_active_workers,
- (uint) _workers,
+ _workers,
_created_workers,
worker_type,
initializing);
_active_workers = MIN2(_created_workers, _active_workers);
+
+ WorkerManager::log_worker_creation(this, previous_created_workers, _active_workers, _created_workers, initializing);
+}
+
+const char* GCTaskManager::group_name() {
+ return "ParGC Thread";
}
void GCTaskManager::initialize() {
diff --git a/hotspot/src/share/vm/gc/parallel/gcTaskManager.hpp b/hotspot/src/share/vm/gc/parallel/gcTaskManager.hpp
index 70ac6b2d226..d05869a3730 100644
--- a/hotspot/src/share/vm/gc/parallel/gcTaskManager.hpp
+++ b/hotspot/src/share/vm/gc/parallel/gcTaskManager.hpp
@@ -556,6 +556,8 @@ protected:
GCTaskThread* install_worker(uint worker_id);
// Add GC workers as needed.
void add_workers(bool initializing);
+ // Base name (without worker id #) of threads.
+ const char* group_name();
};
//
diff --git a/hotspot/src/share/vm/gc/parallel/gcTaskThread.cpp b/hotspot/src/share/vm/gc/parallel/gcTaskThread.cpp
index f6fd1a38a6f..42e67119e0a 100644
--- a/hotspot/src/share/vm/gc/parallel/gcTaskThread.cpp
+++ b/hotspot/src/share/vm/gc/parallel/gcTaskThread.cpp
@@ -45,7 +45,7 @@ GCTaskThread::GCTaskThread(GCTaskManager* manager,
_time_stamp_index(0)
{
set_id(which);
- set_name("ParGC Thread#%d", which);
+ set_name("%s#%d", manager->group_name(), which);
}
GCTaskThread::~GCTaskThread() {
diff --git a/hotspot/src/share/vm/gc/parallel/gcTaskThread.hpp b/hotspot/src/share/vm/gc/parallel/gcTaskThread.hpp
index aa10a69531a..59e6286f26e 100644
--- a/hotspot/src/share/vm/gc/parallel/gcTaskThread.hpp
+++ b/hotspot/src/share/vm/gc/parallel/gcTaskThread.hpp
@@ -55,6 +55,7 @@ private:
return new GCTaskThread(manager, which, processor_id);
}
public:
+
static void destroy(GCTaskThread* manager) {
if (manager != NULL) {
delete manager;
diff --git a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp
index 2b2fe90598d..303252369e4 100644
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp
@@ -159,6 +159,8 @@ class CollectedHeap : public CHeapObj {
inline static void post_allocation_setup_array(KlassHandle klass,
HeapWord* obj, int length);
+ inline static void post_allocation_setup_class(KlassHandle klass, HeapWord* obj, int size);
+
// Clears an allocated object.
inline static void init_obj(HeapWord* obj, size_t size);
@@ -300,6 +302,7 @@ class CollectedHeap : public CHeapObj {
inline static oop obj_allocate(KlassHandle klass, int size, TRAPS);
inline static oop array_allocate(KlassHandle klass, int size, int length, TRAPS);
inline static oop array_allocate_nozero(KlassHandle klass, int size, int length, TRAPS);
+ inline static oop class_allocate(KlassHandle klass, int size, TRAPS);
inline static void post_allocation_install_obj_klass(KlassHandle klass,
oop obj);
diff --git a/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp b/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
index 71b3c360044..36ef86f98c9 100644
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp
@@ -25,6 +25,7 @@
#ifndef SHARE_VM_GC_SHARED_COLLECTEDHEAP_INLINE_HPP
#define SHARE_VM_GC_SHARED_COLLECTEDHEAP_INLINE_HPP
+#include "classfile/javaClasses.hpp"
#include "gc/shared/allocTracer.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/threadLocalAllocBuffer.inline.hpp"
@@ -96,6 +97,22 @@ void CollectedHeap::post_allocation_setup_obj(KlassHandle klass,
post_allocation_notify(klass, (oop)obj, size);
}
+void CollectedHeap::post_allocation_setup_class(KlassHandle klass,
+ HeapWord* obj,
+ int size) {
+ // Set oop_size field before setting the _klass field
+ // in post_allocation_setup_common() because the klass field
+ // indicates that the object is parsable by concurrent GC.
+ oop new_cls = (oop)obj;
+ assert(size > 0, "oop_size must be positive.");
+ java_lang_Class::set_oop_size(new_cls, size);
+ post_allocation_setup_common(klass, obj);
+ assert(Universe::is_bootstrapping() ||
+ !new_cls->is_array(), "must not be an array");
+ // notify jvmti and dtrace
+ post_allocation_notify(klass, new_cls, size);
+}
+
void CollectedHeap::post_allocation_setup_array(KlassHandle klass,
HeapWord* obj,
int length) {
@@ -207,6 +224,16 @@ oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
return (oop)obj;
}
+oop CollectedHeap::class_allocate(KlassHandle 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
+ NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
+ return (oop)obj;
+}
+
oop CollectedHeap::array_allocate(KlassHandle klass,
int size,
int length,
diff --git a/hotspot/src/share/vm/gc/shared/workerManager.hpp b/hotspot/src/share/vm/gc/shared/workerManager.hpp
index 9dfa7d745bc..e4ac4659dcf 100644
--- a/hotspot/src/share/vm/gc/shared/workerManager.hpp
+++ b/hotspot/src/share/vm/gc/shared/workerManager.hpp
@@ -47,18 +47,18 @@ class WorkerManager : public AllStatic {
// threads and a failure would not be optimal but should not be fatal.
template
static uint add_workers (WorkerType* holder,
- uint active_workers,
- uint total_workers,
- uint created_workers,
- os::ThreadType worker_type,
- bool initializing) {
+ uint active_workers,
+ uint total_workers,
+ uint created_workers,
+ os::ThreadType worker_type,
+ bool initializing) {
uint start = created_workers;
uint end = MIN2(active_workers, total_workers);
for (uint worker_id = start; worker_id < end; worker_id += 1) {
WorkerThread* new_worker = holder->install_worker(worker_id);
assert(new_worker != NULL, "Failed to allocate GangWorker");
if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) {
- if(initializing) {
+ if (initializing) {
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR,
"Cannot create worker GC thread. Out of system resources.");
}
@@ -67,11 +67,21 @@ class WorkerManager : public AllStatic {
os::start_thread(new_worker);
}
- log_trace(gc, task)("AdaptiveSizePolicy::add_workers() : "
- "active_workers: %u created_workers: %u",
- active_workers, created_workers);
-
return created_workers;
}
+
+ // Log (at trace level) a change in the number of created workers.
+ template
+ static void log_worker_creation(WorkerType* holder,
+ uint previous_created_workers,
+ uint active_workers,
+ uint created_workers,
+ bool initializing) {
+ if (previous_created_workers < created_workers) {
+ const char* initializing_msg = initializing ? "Adding initial" : "Creating additional";
+ log_trace(gc, task)("%s %s(s) previously created workers %u active workers %u total created workers %u",
+ initializing_msg, holder->group_name(), previous_created_workers, active_workers, created_workers);
+ }
+ }
};
#endif // SHARE_VM_GC_SHARED_WORKERMANAGER_HPP
diff --git a/hotspot/src/share/vm/gc/shared/workgroup.cpp b/hotspot/src/share/vm/gc/shared/workgroup.cpp
index 053c8a1b8cf..42334dc71e3 100644
--- a/hotspot/src/share/vm/gc/shared/workgroup.cpp
+++ b/hotspot/src/share/vm/gc/shared/workgroup.cpp
@@ -66,6 +66,7 @@ void AbstractWorkGang::add_workers(uint active_workers, bool initializing) {
} else {
worker_type = os::pgc_thread;
}
+ uint previous_created_workers = _created_workers;
_created_workers = WorkerManager::add_workers(this,
active_workers,
@@ -74,6 +75,8 @@ void AbstractWorkGang::add_workers(uint active_workers, bool initializing) {
worker_type,
initializing);
_active_workers = MIN2(_created_workers, _active_workers);
+
+ WorkerManager::log_worker_creation(this, previous_created_workers, _active_workers, _created_workers, initializing);
}
AbstractGangWorker* AbstractWorkGang::worker(uint i) const {
diff --git a/hotspot/src/share/vm/gc/shared/workgroup.hpp b/hotspot/src/share/vm/gc/shared/workgroup.hpp
index 150365f4e04..1208d42a970 100644
--- a/hotspot/src/share/vm/gc/shared/workgroup.hpp
+++ b/hotspot/src/share/vm/gc/shared/workgroup.hpp
@@ -176,6 +176,9 @@ class AbstractWorkGang : public CHeapObj {
// Return the Ith worker.
AbstractGangWorker* worker(uint i) const;
+ // Base name (without worker id #) of threads.
+ const char* group_name() { return name(); }
+
void threads_do(ThreadClosure* tc) const;
// Create a GC worker and install it into the work gang.
diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.cpp b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp
index bf03c08c2bf..6f6d7ea8d3c 100644
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.cpp
+++ b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,12 +31,12 @@ Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) {
// set next bytecode position
address bcp = RawBytecodeStream::bcp();
address end = method()->code_base() + end_bci();
- int l = Bytecodes::raw_special_length_at(bcp, end);
- if (l <= 0 || (_bci + l) > _end_bci) {
+ int len = Bytecodes::raw_special_length_at(bcp, end);
+ // Very large tableswitch or lookupswitch size can cause _next_bci to overflow.
+ if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
code = Bytecodes::_illegal;
} else {
- _next_bci += l;
- assert(_bci < _next_bci, "length must be > 0");
+ _next_bci += len;
// set attributes
_is_wide = false;
// check for special (uncommon) cases
diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp
index 23d0a651494..9e7a4c42697 100644
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp
+++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -135,12 +135,15 @@ class RawBytecodeStream: public BaseBytecodeStream {
code = Bytecodes::code_or_bp_at(bcp);
// set next bytecode position
- int l = Bytecodes::length_for(code);
- if (l > 0 && (_bci + l) <= _end_bci) {
+ int len = Bytecodes::length_for(code);
+ if (len > 0 && (_bci <= _end_bci - len)) {
assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch
&& code != Bytecodes::_lookupswitch, "can't be special bytecode");
_is_wide = false;
- _next_bci += l;
+ _next_bci += len;
+ if (_next_bci <= _bci) { // Check for integer overflow
+ code = Bytecodes::_illegal;
+ }
_raw_code = code;
return code;
} else {
@@ -189,19 +192,23 @@ class BytecodeStream: public BaseBytecodeStream {
// note that we cannot advance before having the
// tty bytecode otherwise the stepping is wrong!
// (carefull: length_for(...) must be used first!)
- int l = Bytecodes::length_for(code);
- if (l == 0) l = Bytecodes::length_at(_method(), bcp);
- _next_bci += l;
- assert(_bci < _next_bci, "length must be > 0");
- // set attributes
- _is_wide = false;
- // check for special (uncommon) cases
- if (code == Bytecodes::_wide) {
- raw_code = (Bytecodes::Code)bcp[1];
- code = raw_code; // wide BCs are always Java-normal
- _is_wide = true;
+ int len = Bytecodes::length_for(code);
+ if (len == 0) len = Bytecodes::length_at(_method(), bcp);
+ if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
+ raw_code = code = Bytecodes::_illegal;
+ } else {
+ _next_bci += len;
+ assert(_bci < _next_bci, "length must be > 0");
+ // set attributes
+ _is_wide = false;
+ // check for special (uncommon) cases
+ if (code == Bytecodes::_wide) {
+ raw_code = (Bytecodes::Code)bcp[1];
+ code = raw_code; // wide BCs are always Java-normal
+ _is_wide = true;
+ }
+ assert(Bytecodes::is_java_code(code), "sanity check");
}
- assert(Bytecodes::is_java_code(code), "sanity check");
}
_raw_code = raw_code;
_code = code;
diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
index 77309bb34bb..0770b397d79 100644
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
@@ -576,27 +576,27 @@ void InterpreterRuntime::resolve_get_put(JavaThread* thread, Bytecodes::Code byt
// compute auxiliary field attributes
TosState state = as_TosState(info.field_type());
- // We need to delay resolving put instructions on final fields
- // until we actually invoke one. This is required so we throw
- // exceptions at the correct place. If we do not resolve completely
- // in the current pass, leaving the put_code set to zero will
- // cause the next put instruction to reresolve.
- Bytecodes::Code put_code = (Bytecodes::Code)0;
-
- // We also need to delay resolving getstatic instructions until the
- // class is intitialized. This is required so that access to the static
+ // Put instructions on final fields are not resolved. This is required so we throw
+ // exceptions at the correct place (when the instruction is actually invoked).
+ // If we do not resolve an instruction in the current pass, leaving the put_code
+ // set to zero will cause the next put instruction to the same field to reresolve.
+ //
+ // Also, we need to delay resolving getstatic and putstatic instructions until the
+ // class is initialized. This is required so that access to the static
// field will call the initialization function every time until the class
// is completely initialized ala. in 2.17.5 in JVM Specification.
InstanceKlass* klass = InstanceKlass::cast(info.field_holder());
bool uninitialized_static = ((bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic) &&
!klass->is_initialized());
- Bytecodes::Code get_code = (Bytecodes::Code)0;
+ Bytecodes::Code put_code = (Bytecodes::Code)0;
+ if (is_put && !info.access_flags().is_final() && !uninitialized_static) {
+ put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
+ }
+
+ Bytecodes::Code get_code = (Bytecodes::Code)0;
if (!uninitialized_static) {
get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
- if (is_put || !info.access_flags().is_final()) {
- put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
- }
}
cp_cache_entry->set_field(
diff --git a/hotspot/src/share/vm/interpreter/linkResolver.cpp b/hotspot/src/share/vm/interpreter/linkResolver.cpp
index ed49b476484..1591012fe2f 100644
--- a/hotspot/src/share/vm/interpreter/linkResolver.cpp
+++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp
@@ -970,7 +970,7 @@ void LinkResolver::resolve_field(fieldDescriptor& fd,
if (is_initialized_static_final_update || is_initialized_instance_final_update) {
ss.print("Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ",
is_static ? "static" : "non-static", resolved_klass()->external_name(), fd.name()->as_C_string(),
- current_klass()->external_name(),
+ m()->name()->as_C_string(),
is_static ? "" : "");
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
}
diff --git a/hotspot/src/share/vm/logging/logConfiguration.cpp b/hotspot/src/share/vm/logging/logConfiguration.cpp
index ecc1ead0105..3081fe93432 100644
--- a/hotspot/src/share/vm/logging/logConfiguration.cpp
+++ b/hotspot/src/share/vm/logging/logConfiguration.cpp
@@ -415,17 +415,8 @@ void LogConfiguration::describe_available(outputStream* out){
void LogConfiguration::describe_current_configuration(outputStream* out){
out->print_cr("Log output configuration:");
for (size_t i = 0; i < _n_outputs; i++) {
- out->print("#" SIZE_FORMAT ": %s ", i, _outputs[i]->name());
- out->print_raw(_outputs[i]->config_string());
- out->print(" ");
- char delimiter[2] = {0};
- for (size_t d = 0; d < LogDecorators::Count; d++) {
- LogDecorators::Decorator decorator = static_cast(d);
- if (_outputs[i]->decorators().is_decorator(decorator)) {
- out->print("%s%s", delimiter, LogDecorators::name(decorator));
- *delimiter = ',';
- }
- }
+ out->print("#" SIZE_FORMAT ": ", i);
+ _outputs[i]->describe(out);
out->cr();
}
}
diff --git a/hotspot/src/share/vm/logging/logFileOutput.cpp b/hotspot/src/share/vm/logging/logFileOutput.cpp
index 3000e61229f..77cad087a0c 100644
--- a/hotspot/src/share/vm/logging/logFileOutput.cpp
+++ b/hotspot/src/share/vm/logging/logFileOutput.cpp
@@ -428,3 +428,13 @@ char* LogFileOutput::make_file_name(const char* file_name,
result[result_len] = '\0';
return result;
}
+
+void LogFileOutput::describe(outputStream *out) {
+ LogOutput::describe(out);
+ out->print(" ");
+
+ out->print("filecount=%u,filesize=" SIZE_FORMAT "%s", _file_count,
+ byte_size_in_proper_unit(_rotate_size),
+ proper_unit_for_byte_size(_rotate_size));
+}
+
diff --git a/hotspot/src/share/vm/logging/logFileOutput.hpp b/hotspot/src/share/vm/logging/logFileOutput.hpp
index 1831016eb6c..3ec3a771985 100644
--- a/hotspot/src/share/vm/logging/logFileOutput.hpp
+++ b/hotspot/src/share/vm/logging/logFileOutput.hpp
@@ -85,6 +85,7 @@ class LogFileOutput : public LogFileStreamOutput {
virtual int write(const LogDecorations& decorations, const char* msg);
virtual int write(LogMessageBuffer::Iterator msg_iterator);
virtual void force_rotate();
+ virtual void describe(outputStream *out);
virtual const char* name() const {
return _name;
diff --git a/hotspot/src/share/vm/logging/logOutput.cpp b/hotspot/src/share/vm/logging/logOutput.cpp
index 0254b7616b5..d17414a6534 100644
--- a/hotspot/src/share/vm/logging/logOutput.cpp
+++ b/hotspot/src/share/vm/logging/logOutput.cpp
@@ -83,3 +83,18 @@ void LogOutput::add_to_config_string(const LogTagSet* ts, LogLevelType level) {
break;
}
}
+
+void LogOutput::describe(outputStream *out) {
+ out->print("%s ", name());
+ out->print_raw(config_string());
+ out->print(" ");
+ char delimiter[2] = {0};
+ for (size_t d = 0; d < LogDecorators::Count; d++) {
+ LogDecorators::Decorator decorator = static_cast(d);
+ if (decorators().is_decorator(decorator)) {
+ out->print("%s%s", delimiter, LogDecorators::name(decorator));
+ *delimiter = ',';
+ }
+ }
+}
+
diff --git a/hotspot/src/share/vm/logging/logOutput.hpp b/hotspot/src/share/vm/logging/logOutput.hpp
index cb5de53c319..24c66156d1b 100644
--- a/hotspot/src/share/vm/logging/logOutput.hpp
+++ b/hotspot/src/share/vm/logging/logOutput.hpp
@@ -83,6 +83,8 @@ class LogOutput : public CHeapObj {
// Do nothing by default.
}
+ virtual void describe(outputStream *out);
+
virtual const char* name() const = 0;
virtual bool initialize(const char* options, outputStream* errstream) = 0;
virtual int write(const LogDecorations& decorations, const char* msg) = 0;
diff --git a/hotspot/src/share/vm/logging/logPrefix.hpp b/hotspot/src/share/vm/logging/logPrefix.hpp
index e4a97ca1fd5..398256753f6 100644
--- a/hotspot/src/share/vm/logging/logPrefix.hpp
+++ b/hotspot/src/share/vm/logging/logPrefix.hpp
@@ -74,6 +74,7 @@ DEBUG_ONLY(size_t Test_log_prefix_prefixer(char* buf, size_t len);)
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ref)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ref, start)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, start)) \
+ LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, stringtable)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, sweep)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, task)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, task, start)) \
diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
index a87292241b1..5dce2678527 100644
--- a/hotspot/src/share/vm/memory/metaspace.cpp
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
@@ -3106,10 +3106,6 @@ void Metaspace::ergo_initialize() {
assert(MetaspaceSize <= MaxMetaspaceSize, "MetaspaceSize should be limited by MaxMetaspaceSize");
- if (MetaspaceSize < 256*K) {
- vm_exit_during_initialization("Too small initial Metaspace size");
- }
-
MinMetaspaceExpansion = align_size_down_bounded(MinMetaspaceExpansion, _commit_alignment);
MaxMetaspaceExpansion = align_size_down_bounded(MaxMetaspaceExpansion, _commit_alignment);
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp
index 02f025a9630..66366273f19 100644
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp
@@ -60,6 +60,7 @@ bool MetaspaceShared::_link_classes_made_progress;
bool MetaspaceShared::_check_classes_made_progress;
bool MetaspaceShared::_has_error_classes;
bool MetaspaceShared::_archive_loading_failed = false;
+bool MetaspaceShared::_remapped_readwrite = false;
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
SharedMiscRegion MetaspaceShared::_mc;
@@ -806,6 +807,10 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
exit(1);
}
}
+
+ // Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced
+ // Arrays
+ SystemDictionaryShared::finalize_verification_constraints();
}
void MetaspaceShared::prepare_for_dumping() {
@@ -1181,6 +1186,7 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
return false;
}
+ _remapped_readwrite = true;
}
return true;
}
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp
index 34d12eb3794..c60e8207328 100644
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp
@@ -125,6 +125,7 @@ class MetaspaceShared : AllStatic {
static bool _check_classes_made_progress;
static bool _has_error_classes;
static bool _archive_loading_failed;
+ static bool _remapped_readwrite;
static address _cds_i2i_entry_code_buffers;
static size_t _cds_i2i_entry_code_buffers_size;
@@ -205,6 +206,10 @@ class MetaspaceShared : AllStatic {
// sharing is enabled. Simply returns true if sharing is not enabled
// or if the remapping has already been done by a prior call.
static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true);
+ static bool remapped_readwrite() {
+ CDS_ONLY(return _remapped_readwrite);
+ NOT_CDS(return false);
+ }
static void print_shared_spaces();
diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp
index 7d15ab37c49..0310d28c411 100644
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp
@@ -27,6 +27,7 @@
#include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/dependencyContext.hpp"
@@ -597,6 +598,8 @@ bool InstanceKlass::link_class_impl(
// also sets rewritten
this_k->rewrite_class(CHECK_false);
+ } else if (this_k->is_shared()) {
+ SystemDictionaryShared::check_verification_constraints(this_k, CHECK_false);
}
// relocate jsrs and link methods after they are all rewritten
@@ -606,7 +609,12 @@ bool InstanceKlass::link_class_impl(
// methods have been rewritten since rewrite may
// fabricate new Method*s.
// also does loader constraint checking
- if (!this_k()->is_shared()) {
+ //
+ // initialize_vtable and initialize_itable need to be rerun for
+ // a shared class if the class is not loaded by the NULL classloader.
+ ClassLoaderData * loader_data = this_k->class_loader_data();
+ if (!(this_k->is_shared() &&
+ loader_data->is_the_null_class_loader_data())) {
ResourceMark rm(THREAD);
this_k->vtable()->initialize_vtable(true, CHECK_false);
this_k->itable()->initialize_itable(true, CHECK_false);
diff --git a/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp b/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp
index 107f9523296..72edd471c5f 100644
--- a/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp
+++ b/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -50,13 +50,12 @@ instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {
// Query before forming handle.
int size = instance_size(k);
KlassHandle h_k(THREAD, this);
- instanceOop i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
+
+ assert(size > 0, "total object size must be positive: %d", size);
// Since mirrors can be variable sized because of the static fields, store
// the size in the mirror itself.
- java_lang_Class::set_oop_size(i, size);
-
- return i;
+ return (instanceOop)CollectedHeap::class_allocate(h_k, size, CHECK_NULL);
}
int InstanceMirrorKlass::oop_size(oop obj) const {
diff --git a/hotspot/src/share/vm/oops/klassVtable.cpp b/hotspot/src/share/vm/oops/klassVtable.cpp
index 6d0c895f812..73db3001d34 100644
--- a/hotspot/src/share/vm/oops/klassVtable.cpp
+++ b/hotspot/src/share/vm/oops/klassVtable.cpp
@@ -27,6 +27,7 @@
#include "classfile/vmSymbols.hpp"
#include "gc/shared/gcLocker.hpp"
#include "logging/log.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.inline.hpp"
#include "oops/instanceKlass.hpp"
@@ -42,6 +43,10 @@ inline InstanceKlass* klassVtable::ik() const {
return InstanceKlass::cast(_klass());
}
+bool klassVtable::is_preinitialized_vtable() {
+ return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
+}
+
// this function computes the vtable size (including the size needed for miranda
// methods) and the number of miranda methods in this class.
@@ -126,6 +131,12 @@ int klassVtable::index_of(Method* m, int len) const {
int klassVtable::initialize_from_super(KlassHandle super) {
if (super.is_null()) {
return 0;
+ } else if (is_preinitialized_vtable()) {
+ // A shared class' vtable is preinitialized at dump time. No need to copy
+ // methods from super class for shared class, as that was already done
+ // during archiving time. However, if Jvmti has redefined a class,
+ // copy super class's vtable in case the super class has changed.
+ return super->vtable()->length();
} else {
// copy methods from superKlass
klassVtable* superVtable = super->vtable();
@@ -152,6 +163,8 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
KlassHandle super (THREAD, klass()->java_super());
int nofNewEntries = 0;
+ bool is_shared = _klass->is_shared();
+
if (!klass()->is_array_klass()) {
ResourceMark rm(THREAD);
log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string());
@@ -164,6 +177,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
#endif
if (Universe::is_bootstrapping()) {
+ assert(!is_shared, "sanity");
// just clear everything
for (int i = 0; i < _length; i++) table()[i].clear();
return;
@@ -203,6 +217,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
if (len > 0) {
Array* def_vtable_indices = NULL;
if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) {
+ assert(!is_shared, "shared class def_vtable_indices does not exist");
def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
} else {
assert(def_vtable_indices->length() == len, "reinit vtable len?");
@@ -217,7 +232,15 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
// needs new entry
if (needs_new_entry) {
put_method_at(mh(), initialized);
- def_vtable_indices->at_put(i, initialized); //set vtable index
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun for a shared class
+ // (loaded by the non-boot loader) as part of link_class_impl().
+ // The dumptime vtable index should be the same as the runtime index.
+ assert(def_vtable_indices->at(i) == initialized,
+ "dump time vtable index is different from runtime index");
+ } else {
+ def_vtable_indices->at_put(i, initialized); //set vtable index
+ }
initialized++;
}
}
@@ -378,7 +401,8 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
}
// we need a new entry if there is no superclass
- if (klass->super() == NULL) {
+ Klass* super = klass->super();
+ if (super == NULL) {
return allocate_new;
}
@@ -407,7 +431,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
Symbol* target_classname = target_klass->name();
for(int i = 0; i < super_vtable_len; i++) {
- Method* super_method = method_at(i);
+ Method* super_method;
+ if (is_preinitialized_vtable()) {
+ // If this is a shared class, the vtable is already in the final state (fully
+ // initialized). Need to look at the super's vtable.
+ klassVtable* superVtable = super->vtable();
+ super_method = superVtable->method_at(i);
+ } else {
+ super_method = method_at(i);
+ }
// Check if method name matches
if (super_method->name() == name && super_method->signature() == signature) {
@@ -475,7 +507,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
target_method()->set_vtable_index(i);
} else {
if (def_vtable_indices != NULL) {
- def_vtable_indices->at_put(default_index, i);
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader.
+ // The dumptime vtable index should be the same as the runtime index.
+ assert(def_vtable_indices->at(default_index) == i,
+ "dump time vtable index is different from runtime index");
+ } else {
+ def_vtable_indices->at_put(default_index, i);
+ }
}
assert(super_method->is_default_method() || super_method->is_overpass()
|| super_method->is_abstract(), "default override error");
@@ -490,17 +530,26 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
}
void klassVtable::put_method_at(Method* m, int index) {
- if (log_develop_is_enabled(Trace, vtables)) {
- ResourceMark rm;
- outputStream* logst = Log(vtables)::trace_stream();
- const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "";
- logst->print("adding %s at index %d, flags: ", sig, index);
- if (m != NULL) {
- m->print_linkage_flags(logst);
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context. The dumptime
+ // method at the vtable index should be the same as the runtime method.
+ assert(table()[index].method() == m,
+ "archived method is different from the runtime method");
+ } else {
+ if (log_develop_is_enabled(Trace, vtables)) {
+ ResourceMark rm;
+ outputStream* logst = Log(vtables)::trace_stream();
+ const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "";
+ logst->print("adding %s at index %d, flags: ", sig, index);
+ if (m != NULL) {
+ m->print_linkage_flags(logst);
+ }
+ logst->cr();
}
- logst->cr();
+ table()[index].set(m);
}
- table()[index].set(m);
}
// Find out if a method "m" with superclass "super", loader "classloader" and
@@ -950,7 +999,15 @@ bool klassVtable::is_initialized() {
void itableMethodEntry::initialize(Method* m) {
if (m == NULL) return;
- _method = m;
+ if (MetaspaceShared::is_in_shared_space((void*)&_method) &&
+ !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_itable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader.
+ // The dumptime itable method entry should be the same as the runtime entry.
+ assert(_method == m, "sanity");
+ } else {
+ _method = m;
+ }
}
klassItable::klassItable(instanceKlassHandle klass) {
@@ -1054,7 +1111,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) {
logst->cr();
}
if (!m->has_vtable_index()) {
- assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable");
+ // A shared method could have an initialized itable_index that
+ // is < 0.
+ assert(m->vtable_index() == Method::pending_itable_index ||
+ m->is_shared(),
+ "set by initialize_vtable");
m->set_itable_index(ime_num);
// Progress to next itable entry
ime_num++;
@@ -1248,7 +1309,6 @@ void klassItable::dump_itable() {
}
#endif // INCLUDE_JVMTI
-
// Setup
class InterfaceVisiterClosure : public StackObj {
public:
diff --git a/hotspot/src/share/vm/oops/klassVtable.hpp b/hotspot/src/share/vm/oops/klassVtable.hpp
index 0b4e23fa22e..8d33f7960ee 100644
--- a/hotspot/src/share/vm/oops/klassVtable.hpp
+++ b/hotspot/src/share/vm/oops/klassVtable.hpp
@@ -153,6 +153,19 @@ class klassVtable : public ResourceObj {
Array* local_interfaces);
void verify_against(outputStream* st, klassVtable* vt, int index);
inline InstanceKlass* ik() const;
+ // When loading a class from CDS archive at run time, and no class redefintion
+ // has happened, it is expected that the class's itable/vtables are
+ // laid out exactly the same way as they had been during dump time.
+ // Therefore, in klassVtable::initialize_[iv]table, we do not layout the
+ // tables again. Instead, we only rerun the process to create/check
+ // the class loader constraints. In non-product builds, we add asserts to
+ // guarantee that the table's layout would be the same as at dump time.
+ //
+ // If JVMTI redefines any class, the read-only shared memory are remapped
+ // as read-write. A shared class' vtable/itable are re-initialized and
+ // might have different layout due to class redefinition of the shared class
+ // or its super types.
+ bool is_preinitialized_vtable();
};
diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp
index 1a33bab678f..9bcf81fe0f7 100644
--- a/hotspot/src/share/vm/oops/method.cpp
+++ b/hotspot/src/share/vm/oops/method.cpp
@@ -313,6 +313,33 @@ void Method::remove_unshareable_info() {
unlink_method();
}
+void Method::set_vtable_index(int index) {
+ if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context.
+ return; // don't write into the shared class
+ } else {
+ _vtable_index = index;
+ }
+}
+
+void Method::set_itable_index(int index) {
+ if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_itable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context. The dumptime
+ // itable index should be the same as the runtime index.
+ assert(_vtable_index == itable_index_max - index,
+ "archived itable index is different from runtime index");
+ return; // don’t write into the shared class
+ } else {
+ _vtable_index = itable_index_max - index;
+ }
+ assert(valid_itable_index(), "");
+}
+
+
bool Method::was_executed_more_than(int n) {
// Invocation counter is reset when the Method* is compiled.
diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp
index 5a635f8935f..92cca9e3839 100644
--- a/hotspot/src/share/vm/oops/method.hpp
+++ b/hotspot/src/share/vm/oops/method.hpp
@@ -470,12 +470,12 @@ class Method : public Metadata {
DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; })
bool has_vtable_index() const { return _vtable_index >= 0; }
int vtable_index() const { return _vtable_index; }
- void set_vtable_index(int index) { _vtable_index = index; }
+ void set_vtable_index(int index);
DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; })
bool has_itable_index() const { return _vtable_index <= itable_index_max; }
int itable_index() const { assert(valid_itable_index(), "");
return itable_index_max - _vtable_index; }
- void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); }
+ void set_itable_index(int index);
// interpreter entry
address interpreter_entry() const { return _i2i_entry; }
diff --git a/hotspot/src/share/vm/oops/oop.inline.hpp b/hotspot/src/share/vm/oops/oop.inline.hpp
index c3072a1b21c..fe3cd508a9f 100644
--- a/hotspot/src/share/vm/oops/oop.inline.hpp
+++ b/hotspot/src/share/vm/oops/oop.inline.hpp
@@ -258,8 +258,8 @@ int oopDesc::size_given_klass(Klass* klass) {
}
}
- assert(s % MinObjAlignment == 0, "alignment check");
- assert(s > 0, "Bad size calculated");
+ assert(s % MinObjAlignment == 0, "Oop size is not properly aligned: %d", s);
+ assert(s > 0, "Oop size must be greater than zero, not %d", s);
return s;
}
diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp
index cf1e506cf58..af27b917b65 100644
--- a/hotspot/src/share/vm/opto/library_call.cpp
+++ b/hotspot/src/share/vm/opto/library_call.cpp
@@ -2405,8 +2405,13 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c
Compile::AliasType* alias_type = C->alias_type(adr_type);
assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here");
- assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
- alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
+ // Only field, array element or unknown locations are supported.
+ if (alias_type->adr_type() != TypeRawPtr::BOTTOM &&
+ alias_type->adr_type() != TypeOopPtr::BOTTOM &&
+ alias_type->basic_type() == T_ILLEGAL) {
+ return false;
+ }
+
bool mismatched = false;
BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL) {
@@ -2782,12 +2787,6 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
ShouldNotReachHere();
}
- // Null check receiver.
- receiver = null_check(receiver);
- if (stopped()) {
- return true;
- }
-
// Build field offset expression.
// We currently rely on the cookies produced by Unsafe.xxxFieldOffset
// to be plain byte offsets, which are also the same as those accepted
@@ -2799,8 +2798,6 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
const TypePtr *adr_type = _gvn.type(adr)->isa_ptr();
Compile::AliasType* alias_type = C->alias_type(adr_type);
- assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
- alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL &&
((bt == T_OBJECT || bt == T_ARRAY) != (type == T_OBJECT))) {
@@ -2832,6 +2829,12 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
ShouldNotReachHere();
}
+ // Null check receiver.
+ receiver = null_check(receiver);
+ if (stopped()) {
+ return true;
+ }
+
int alias_idx = C->get_alias_index(adr_type);
// Memory-model-wise, a LoadStore acts like a little synchronized
diff --git a/hotspot/src/share/vm/prims/jvmti.xml b/hotspot/src/share/vm/prims/jvmti.xml
index 54af0643f3b..3a06d2fc2b6 100644
--- a/hotspot/src/share/vm/prims/jvmti.xml
+++ b/hotspot/src/share/vm/prims/jvmti.xml
@@ -6509,6 +6509,59 @@ class C2 extends C1 implements I2 {
+
+
+ Get Named Module
+
+ Return the java.lang.reflect.Module object for a named
+ module defined to a class loader that contains a given package.
+ The module is returned via module_ptr.
+
+ If a named module is defined to the class loader and it
+ contains the package then that named module is returned,
+ otherwise NULL is returned.
+
+
+ new
+
+
+
+
+
+
+ the bootstrap loader is assumed
+
+
+ A class loader.
+ If the class_loader is not NULL
+ or a subclass of java.lang.ClassLoader
+ this function returns
+ .
+
+
+
+
+
+ The name of the package, encoded as a
+ modified UTF-8 string.
+ The package name is in internal form (JVMS 4.2.1);
+ identifiers are separated by forward slashes rather than periods.
+
+
+
+
+
+ On return, points to a java.lang.reflect.Module object
+ or points to NULL.
+
+
+
+
+
+ If class loader is not NULL and is not a class loader object.
+
+
+
@@ -12462,6 +12515,14 @@ myInit() {
new_class_data has been set, it becomes the
class_data for the next agent.
+ When handling a class load in the live phase, then the
+
+ function can be used to map class loader and a package name to a module.
+ When a class is being redefined or retransformed then
+ class_being_redefined is non NULL and so
+ the JNI GetModule function can also be used
+ to obtain the Module.
+
The order that this event is sent to each environment differs
from other events.
This event is sent to environments in the following order:
@@ -14427,20 +14488,15 @@ typedef void (JNICALL *jvmtiEventVMInit)
Added support for statically linked agents.
-
+
Support for modules:
- The majorversion is 9 now
- The ClassFileLoadHook events are not sent during the primordial phase anymore.
- Add new function GetAllModules
-
-
- Support for modules:
- Add new capability can_generate_early_vmstart
- Allow CompiledMethodLoad events at start phase
-
-
- Support for modules:
- Add new capability can_generate_early_class_hook_events
+ - Add new function GetNamedModule
diff --git a/hotspot/src/share/vm/prims/jvmtiEnv.cpp b/hotspot/src/share/vm/prims/jvmtiEnv.cpp
index 082d531daf1..2a5b202d93f 100644
--- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp
+++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/classLoaderExt.hpp"
+#include "classfile/modules.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "interpreter/bytecodeStream.hpp"
@@ -201,6 +202,28 @@ JvmtiEnv::GetAllModules(jint* module_count_ptr, jobject** modules_ptr) {
} /* end GetAllModules */
+// class_loader - NULL is a valid value, must be pre-checked
+// package_name - pre-checked for NULL
+// module_ptr - pre-checked for NULL
+jvmtiError
+JvmtiEnv::GetNamedModule(jobject class_loader, const char* package_name, jobject* module_ptr) {
+ JavaThread* THREAD = JavaThread::current(); // pass to macros
+ ResourceMark rm(THREAD);
+
+ Handle h_loader (THREAD, JNIHandles::resolve(class_loader));
+ // Check that loader is a subclass of java.lang.ClassLoader.
+ if (h_loader.not_null() && !java_lang_ClassLoader::is_subclass(h_loader->klass())) {
+ return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+ }
+ jobject module = Modules::get_named_module(h_loader, package_name, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return JVMTI_ERROR_INTERNAL; // unexpected exception
+ }
+ *module_ptr = module;
+ return JVMTI_ERROR_NONE;
+} /* end GetNamedModule */
+
//
// Class functions
//
diff --git a/hotspot/src/share/vm/runtime/objectMonitor.cpp b/hotspot/src/share/vm/runtime/objectMonitor.cpp
index 2cd401b4069..7590d90c9e7 100644
--- a/hotspot/src/share/vm/runtime/objectMonitor.cpp
+++ b/hotspot/src/share/vm/runtime/objectMonitor.cpp
@@ -131,8 +131,6 @@ static int Knob_MoveNotifyee = 2; // notify() - disposition of noti
static int Knob_QMode = 0; // EntryList-cxq policy - queue discipline
static volatile int InitDone = 0;
-#define TrySpin TrySpin_VaryDuration
-
// -----------------------------------------------------------------------------
// Theory of operations -- Monitors lists, thread residency, etc:
//
@@ -1848,13 +1846,8 @@ void ObjectMonitor::notifyAll(TRAPS) {
// hysteresis control to damp the transition rate between spinning and
// not spinning.
-intptr_t ObjectMonitor::SpinCallbackArgument = 0;
-int (*ObjectMonitor::SpinCallbackFunction)(intptr_t, int) = NULL;
-
// Spinning: Fixed frequency (100%), vary duration
-
-
-int ObjectMonitor::TrySpin_VaryDuration(Thread * Self) {
+int ObjectMonitor::TrySpin(Thread * Self) {
// Dumb, brutal spin. Good for comparative measurements against adaptive spinning.
int ctr = Knob_FixedSpin;
if (ctr != 0) {
@@ -1948,11 +1941,6 @@ int ObjectMonitor::TrySpin_VaryDuration(Thread * Self) {
goto Abort; // abrupt spin egress
}
if (Knob_UsePause & 1) SpinPause();
-
- int (*scb)(intptr_t,int) = SpinCallbackFunction;
- if (hits > 50 && scb != NULL) {
- int abend = (*scb)(SpinCallbackArgument, 0);
- }
}
if (Knob_UsePause & 2) SpinPause();
diff --git a/hotspot/src/share/vm/runtime/objectMonitor.hpp b/hotspot/src/share/vm/runtime/objectMonitor.hpp
index 0ca2a1e5595..253887f8ea6 100644
--- a/hotspot/src/share/vm/runtime/objectMonitor.hpp
+++ b/hotspot/src/share/vm/runtime/objectMonitor.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -161,9 +161,6 @@ class ObjectMonitor {
Thread * volatile _Responsible;
volatile int _Spinner; // for exit->spinner handoff optimization
- volatile int _SpinFreq; // Spin 1-out-of-N attempts: success rate
- volatile int _SpinClock;
- volatile intptr_t _SpinState; // MCS/CLH list of spinners
volatile int _SpinDuration;
volatile jint _count; // reference count to prevent reclamation/deflation
@@ -238,10 +235,6 @@ class ObjectMonitor {
static int cxq_offset_in_bytes() { return offset_of(ObjectMonitor, _cxq); }
static int succ_offset_in_bytes() { return offset_of(ObjectMonitor, _succ); }
static int EntryList_offset_in_bytes() { return offset_of(ObjectMonitor, _EntryList); }
- static int FreeNext_offset_in_bytes() { return offset_of(ObjectMonitor, FreeNext); }
- static int WaitSet_offset_in_bytes() { return offset_of(ObjectMonitor, _WaitSet); }
- static int Responsible_offset_in_bytes() { return offset_of(ObjectMonitor, _Responsible); }
- static int Spinner_offset_in_bytes() { return offset_of(ObjectMonitor, _Spinner); }
// ObjectMonitor references can be ORed with markOopDesc::monitor_value
// as part of the ObjectMonitor tagging mechanism. When we combine an
@@ -257,11 +250,6 @@ class ObjectMonitor {
#define OM_OFFSET_NO_MONITOR_VALUE_TAG(f) \
((ObjectMonitor::f ## _offset_in_bytes()) - markOopDesc::monitor_value)
- // Eventually we'll make provisions for multiple callbacks, but
- // now one will suffice.
- static int (*SpinCallbackFunction)(intptr_t, int);
- static intptr_t SpinCallbackArgument;
-
markOop header() const;
void set_header(markOop hdr);
@@ -312,8 +300,6 @@ class ObjectMonitor {
_cxq = NULL;
_WaitSet = NULL;
_recursions = 0;
- _SpinFreq = 0;
- _SpinClock = 0;
}
public:
@@ -353,9 +339,7 @@ class ObjectMonitor {
void UnlinkAfterAcquire(Thread * Self, ObjectWaiter * SelfNode);
int TryLock(Thread * Self);
int NotRunnable(Thread * Self, Thread * Owner);
- int TrySpin_Fixed(Thread * Self);
- int TrySpin_VaryFrequency(Thread * Self);
- int TrySpin_VaryDuration(Thread * Self);
+ int TrySpin(Thread * Self);
void ExitEpilog(Thread * Self, ObjectWaiter * Wakee);
bool ExitSuspendEquivalent(JavaThread * Self);
void post_monitor_wait_event(EventJavaMonitorWait * event,
diff --git a/hotspot/src/share/vm/runtime/synchronizer.hpp b/hotspot/src/share/vm/runtime/synchronizer.hpp
index 777317b927c..af242e8557a 100644
--- a/hotspot/src/share/vm/runtime/synchronizer.hpp
+++ b/hotspot/src/share/vm/runtime/synchronizer.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,8 +144,6 @@ class ObjectSynchronizer : AllStatic {
static void verify() PRODUCT_RETURN;
static int verify_objmon_isinpool(ObjectMonitor *addr) PRODUCT_RETURN0;
- static void RegisterSpinCallback(int(*)(intptr_t, int), intptr_t);
-
private:
enum { _BLOCKSIZE = 128 };
// global list of blocks of monitors
diff --git a/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh b/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh
index 85ce38b6605..fd0bd005fc2 100644
--- a/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh
+++ b/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh
@@ -29,6 +29,7 @@
## @summary testing of ciReplay with using generated by VM replay.txt w/o comp_level
## @author igor.ignatyev@oracle.com
## @requires vm.flightRecorder != true
+## @ignore 8157984
## @run shell TestVM_no_comp_level.sh
##
diff --git a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
index b30686dfe71..ab9e0b786c5 100644
--- a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
+++ b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
@@ -25,6 +25,7 @@
* @test
* @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64")
* @library ../../../../../
+ * @ignore 8161550
* @modules java.base/jdk.internal.reflect
* jdk.vm.ci/jdk.vm.ci.meta
* jdk.vm.ci/jdk.vm.ci.runtime
diff --git a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java
index 33ff3e01d93..0ed34a46c7b 100644
--- a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java
+++ b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java
@@ -28,6 +28,7 @@
* @library /testlibrary /test/lib /
* @modules java.base/jdk.internal.misc
* java.management
+ * @ignore 8157984
* @build TestRangeCheckSmearing
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* jdk.test.lib.Platform
diff --git a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java
index 96f52576b4f..9c471c69618 100644
--- a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java
+++ b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java
@@ -26,6 +26,7 @@
* @library /testlibrary /test/lib /
* @modules java.base/jdk.internal.misc
* @modules java.management
+ * @ignore 8157984
* @build NonTieredLevelsTest
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
diff --git a/hotspot/test/compiler/unsafe/OpaqueAccesses.java b/hotspot/test/compiler/unsafe/OpaqueAccesses.java
new file mode 100644
index 00000000000..bc274e5399b
--- /dev/null
+++ b/hotspot/test/compiler/unsafe/OpaqueAccesses.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8155781
+ * @modules java.base/jdk.internal.misc
+ *
+ * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ * -XX:-TieredCompilation -Xbatch
+ * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*
+ * compiler.unsafe.OpaqueAccesses
+ */
+package compiler.unsafe;
+
+import jdk.internal.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+public class OpaqueAccesses {
+ private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+ private static final Object INSTANCE = new OpaqueAccesses();
+
+ private static final Object[] ARRAY = new Object[10];
+
+ private static final long F_OFFSET;
+ private static final long E_OFFSET;
+
+ static {
+ try {
+ Field field = OpaqueAccesses.class.getDeclaredField("f");
+ F_OFFSET = UNSAFE.objectFieldOffset(field);
+
+ E_OFFSET = UNSAFE.arrayBaseOffset(ARRAY.getClass());
+ } catch (NoSuchFieldException e) {
+ throw new Error(e);
+ }
+ }
+
+ private Object f = new Object();
+
+ static Object testFixedOffsetField(Object o) {
+ return UNSAFE.getObject(o, F_OFFSET);
+ }
+
+ static int testFixedOffsetHeader0(Object o) {
+ return UNSAFE.getInt(o, 0);
+ }
+
+ static int testFixedOffsetHeader4(Object o) {
+ return UNSAFE.getInt(o, 4);
+ }
+
+ static Object testFixedBase(long off) {
+ return UNSAFE.getObject(INSTANCE, off);
+ }
+
+ static Object testOpaque(Object o, long off) {
+ return UNSAFE.getObject(o, off);
+ }
+
+ static int testFixedOffsetHeaderArray0(Object[] arr) {
+ return UNSAFE.getInt(arr, 0);
+ }
+
+ static int testFixedOffsetHeaderArray4(Object[] arr) {
+ return UNSAFE.getInt(arr, 4);
+ }
+
+ static Object testFixedOffsetArray(Object[] arr) {
+ return UNSAFE.getObject(arr, E_OFFSET);
+ }
+
+ static Object testFixedBaseArray(long off) {
+ return UNSAFE.getObject(ARRAY, off);
+ }
+
+ static Object testOpaqueArray(Object[] o, long off) {
+ return UNSAFE.getObject(o, off);
+ }
+
+ static final long ADDR = UNSAFE.allocateMemory(10);
+ static boolean flag;
+
+ static int testMixedAccess() {
+ flag = !flag;
+ Object o = (flag ? INSTANCE : null);
+ long off = (flag ? F_OFFSET : ADDR);
+ return UNSAFE.getInt(o, off);
+ }
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 20_000; i++) {
+ // Instance
+ testFixedOffsetField(INSTANCE);
+ testFixedOffsetHeader0(INSTANCE);
+ testFixedOffsetHeader4(INSTANCE);
+ testFixedBase(F_OFFSET);
+ testOpaque(INSTANCE, F_OFFSET);
+ testMixedAccess();
+
+ // Array
+ testFixedOffsetHeaderArray0(ARRAY);
+ testFixedOffsetHeaderArray4(ARRAY);
+ testFixedOffsetArray(ARRAY);
+ testFixedBaseArray(E_OFFSET);
+ testOpaqueArray(ARRAY, E_OFFSET);
+ }
+ System.out.println("TEST PASSED");
+ }
+}
diff --git a/hotspot/test/gc/TestSmallHeap.java b/hotspot/test/gc/TestSmallHeap.java
index a58fa8c4e28..4b3d403d9be 100644
--- a/hotspot/test/gc/TestSmallHeap.java
+++ b/hotspot/test/gc/TestSmallHeap.java
@@ -27,6 +27,7 @@
* @requires vm.gc=="null"
* @summary Verify that starting the VM with a small heap works
* @library /testlibrary /test/lib /test/lib/share/classes
+ * @ignore 8161552
* @modules java.base/jdk.internal.misc
* @modules java.management/sun.management
* @build TestSmallHeap
diff --git a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java
index af0b2a7922c..d393a75323b 100644
--- a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java
+++ b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java
@@ -29,6 +29,7 @@
* parallel collectors.
* @requires vm.gc=="null"
* @library /testlibrary /test/lib
+ * @ignore 8161552
* @modules java.base/jdk.internal.misc
* java.management
* @build TestParallelHeapSizeFlags TestMaxHeapSizeTools
diff --git a/hotspot/test/gc/ergonomics/TestInitialGCThreadLogging.java b/hotspot/test/gc/ergonomics/TestInitialGCThreadLogging.java
new file mode 100644
index 00000000000..29a7602f83f
--- /dev/null
+++ b/hotspot/test/gc/ergonomics/TestInitialGCThreadLogging.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestInitialGCThreadLogging
+ * @bug 8157240
+ * @summary Check trace logging of initial GC threads.
+ * @requires vm.gc=="null"
+ * @key gc
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ */
+
+import jdk.test.lib.ProcessTools;
+import jdk.test.lib.OutputAnalyzer;
+
+public class TestInitialGCThreadLogging {
+ public static void main(String[] args) throws Exception {
+
+ testInitialGCThreadLogging("UseConcMarkSweepGC", "GC Thread");
+
+ testInitialGCThreadLogging("UseG1GC", "GC Thread");
+
+ testInitialGCThreadLogging("UseParallelGC", "ParGC Thread");
+ }
+
+ private static void verifyDynamicNumberOfGCThreads(OutputAnalyzer output, String threadName) {
+ output.shouldHaveExitValue(0); // test should run succesfully
+ output.shouldContain(threadName);
+ }
+
+ private static void testInitialGCThreadLogging(String gcFlag, String threadName) throws Exception {
+ // UseDynamicNumberOfGCThreads and TraceDynamicGCThreads enabled
+ String[] baseArgs = {"-XX:+" + gcFlag, "-Xmx10M", "-XX:+UseDynamicNumberOfGCThreads", "-Xlog:gc+task=trace", "-version"};
+
+ // Base test with gc and +UseDynamicNumberOfGCThreads:
+ ProcessBuilder pb_enabled = ProcessTools.createJavaProcessBuilder(baseArgs);
+ verifyDynamicNumberOfGCThreads(new OutputAnalyzer(pb_enabled.start()), threadName);
+ }
+}
diff --git a/hotspot/test/gc/g1/TestStringSymbolTableStats.java b/hotspot/test/gc/g1/TestStringSymbolTableStats.java
index d3c7056387e..e86187988b3 100644
--- a/hotspot/test/gc/g1/TestStringSymbolTableStats.java
+++ b/hotspot/test/gc/g1/TestStringSymbolTableStats.java
@@ -46,7 +46,7 @@ public class TestStringSymbolTableStats {
System.out.println("Output:\n" + output.getOutput());
- output.shouldContain("Cleaned string and symbol table");
+ output.shouldMatch("GC\\(\\d+\\) Cleaned string and symbol table");
output.shouldHaveExitValue(0);
}
diff --git a/hotspot/test/gc/metaspace/TestMetaspaceInitialization.java b/hotspot/test/gc/metaspace/TestMetaspaceInitialization.java
index e38ef160acd..f6ac4141e81 100644
--- a/hotspot/test/gc/metaspace/TestMetaspaceInitialization.java
+++ b/hotspot/test/gc/metaspace/TestMetaspaceInitialization.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,11 +24,11 @@
import java.util.ArrayList;
/* @test TestMetaspaceInitialization
- * @bug 8042933
+ * @bug 8024945
* @summary Tests to initialize metaspace with a very low MetaspaceSize
* @modules java.base/jdk.internal.misc
* @library /testlibrary
- * @run main/othervm -XX:MetaspaceSize=2m TestMetaspaceInitialization
+ * @run main/othervm -XX:MetaspaceSize=0 TestMetaspaceInitialization
*/
public class TestMetaspaceInitialization {
private class Internal {
diff --git a/hotspot/test/gc/metaspace/TestMetaspaceSizeFlags.java b/hotspot/test/gc/metaspace/TestMetaspaceSizeFlags.java
index bb76ae4bd7b..b56343c271f 100644
--- a/hotspot/test/gc/metaspace/TestMetaspaceSizeFlags.java
+++ b/hotspot/test/gc/metaspace/TestMetaspaceSizeFlags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,9 +47,6 @@ public class TestMetaspaceSizeFlags {
// 8024650: MaxMetaspaceSize was adjusted instead of MetaspaceSize.
testMaxMetaspaceSizeLTMetaspaceSize(MAX_ALIGNMENT, MAX_ALIGNMENT * 2);
testMaxMetaspaceSizeGTMetaspaceSize(MAX_ALIGNMENT * 2, MAX_ALIGNMENT);
- testTooSmallInitialMetaspace(0, 0);
- testTooSmallInitialMetaspace(0, MAX_ALIGNMENT);
- testTooSmallInitialMetaspace(MAX_ALIGNMENT, 0);
}
private static void testMaxMetaspaceSizeEQMetaspaceSize(long maxMetaspaceSize, long metaspaceSize) throws Exception {
@@ -73,11 +70,6 @@ public class TestMetaspaceSizeFlags {
Asserts.assertEQ(mf.metaspaceSize, metaspaceSize);
}
- private static void testTooSmallInitialMetaspace(long maxMetaspaceSize, long metaspaceSize) throws Exception {
- OutputAnalyzer output = run(maxMetaspaceSize, metaspaceSize);
- output.shouldContain("Too small initial Metaspace size");
- }
-
private static MetaspaceFlags runAndGetValue(long maxMetaspaceSize, long metaspaceSize) throws Exception {
OutputAnalyzer output = run(maxMetaspaceSize, metaspaceSize);
output.shouldNotMatch("Error occurred during initialization of VM\n.*");
diff --git a/hotspot/test/runtime/Final/TestPutField.jasm b/hotspot/test/runtime/Final/TestPutField.jasm
new file mode 100644
index 00000000000..5d371f30f28
--- /dev/null
+++ b/hotspot/test/runtime/Final/TestPutField.jasm
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+public class TestPutField
+version 53:0
+{
+
+final Field test_field:"I";
+
+
+public Method :"()V"
+ stack 2 locals 1
+{
+ aload_0;
+ dup;
+ invokespecial Method java/lang/Object.:"()V";
+ bipush 13;
+ putfield Field test_field:"I";
+ return;
+}
+
+public Method aMethod:"()I"
+ stack 2 locals 2
+{
+ aload_0;
+ getfield Field test_field:"I";
+ istore_1;
+ aload_0;
+ bipush 14;
+ putfield Field test_field:"I";
+ iload_1;
+ ireturn;
+}
+
+
+public static Method test:"()V"
+ stack 2 locals 2
+{
+ new class TestPutField;
+ astore_0;
+ aload_0;
+ invokespecial Method :"()V";
+ getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
+ astore_1;
+ aload_1;
+ aload_0;
+ invokevirtual Method aMethod:"()I";
+ invokevirtual Method java/io/PrintStream.println:"(I)V";
+ aload_1;
+ aload_0;
+ getfield Field test_field:"I";
+ invokevirtual Method java/io/PrintStream.println:"(I)V";
+ return;
+}
+
+}
diff --git a/hotspot/test/runtime/Final/TestPutMain.java b/hotspot/test/runtime/Final/TestPutMain.java
new file mode 100644
index 00000000000..601a6dbad3b
--- /dev/null
+++ b/hotspot/test/runtime/Final/TestPutMain.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @bug 8160527
+ * @summary The VM does not always perform checks added by 8157181 when updating final instance fields
+ * @library /testlibrary
+ * @compile TestPutField.jasm
+ * @compile TestPutStatic.jasm
+ * @compile TestPutMain.java
+ * @run main/othervm TestPutMain
+ */
+
+import jdk.test.lib.Asserts;
+
+public class TestPutMain {
+ public static void main(String[] args) {
+ boolean exception = false;
+ try {
+ TestPutField.test();
+ } catch (java.lang.IllegalAccessError e) {
+ exception = true;
+ }
+
+ Asserts.assertTrue(exception, "FAILED: Expected IllegalAccessError for illegal update to final instance field was not thrown.");
+
+ exception = false;
+ try {
+ TestPutStatic.test();
+ } catch (java.lang.IllegalAccessError e) {
+ exception = true;
+ }
+
+ Asserts.assertTrue(exception, "FAILED: Expected IllegalAccessError for illegal update to final static field was not thrown.");
+
+ System.out.println("PASSED: Expected IllegalAccessError was thrown.");
+ }
+}
diff --git a/hotspot/test/runtime/Final/TestPutStatic.jasm b/hotspot/test/runtime/Final/TestPutStatic.jasm
new file mode 100644
index 00000000000..ffe6f45c9aa
--- /dev/null
+++ b/hotspot/test/runtime/Final/TestPutStatic.jasm
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+public class TestPutStatic
+version 53:0
+{
+
+final static Field test_field:"I";
+
+
+public static Method :"()V"
+ stack 2 locals 1
+{
+ bipush 13;
+ putstatic Field test_field:"I";
+ return;
+}
+
+public static Method aMethod:"()I"
+ stack 1 locals 1
+{
+ getstatic Field test_field:"I";
+ istore_0;
+ bipush 14;
+ putstatic Field test_field:"I";
+ iload_0;
+ ireturn;
+}
+
+
+public static Method test:"()V"
+ stack 2 locals 1
+{
+ getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
+ astore_0;
+ aload_0;
+ invokestatic Method aMethod:"()I";
+ invokevirtual Method java/io/PrintStream.println:"(I)V";
+ aload_0;
+ getstatic Field test_field:"I";
+ invokevirtual Method java/io/PrintStream.println:"(I)V";
+ return;
+}
+
+}
diff --git a/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java b/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java
index 2cd84536659..b9f2e52ab46 100644
--- a/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java
+++ b/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java
@@ -24,6 +24,9 @@
/*
* @test SASymbolTableTest
* @summary Walk symbol table using SA, with and without CDS.
+ * Started failing on 2016.06.24 due to 8160376 on MacOS X so quarantine
+ * it on that platform:
+ * @requires os.family != "mac"
* @library /testlibrary
* @modules java.base/jdk.internal.misc
* jdk.hotspot.agent/sun.jvm.hotspot.oops
diff --git a/hotspot/test/runtime/Unsafe/GetUnsafe.java b/hotspot/test/runtime/Unsafe/GetUnsafe.java
index 64ad19d8bdd..efc85afa51b 100644
--- a/hotspot/test/runtime/Unsafe/GetUnsafe.java
+++ b/hotspot/test/runtime/Unsafe/GetUnsafe.java
@@ -26,6 +26,7 @@
* @summary Verifies that getUnsafe() actually throws SecurityException when unsafeAccess is prohibited.
* @library /testlibrary
* @modules java.base/jdk.internal.misc
+ * @ignore 8161947
* @run main GetUnsafe
*/
diff --git a/hotspot/test/serviceability/jvmti/GetNamedModule/MyPackage/GetNamedModuleTest.java b/hotspot/test/serviceability/jvmti/GetNamedModule/MyPackage/GetNamedModuleTest.java
new file mode 100644
index 00000000000..80fab0a2ee5
--- /dev/null
+++ b/hotspot/test/serviceability/jvmti/GetNamedModule/MyPackage/GetNamedModuleTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI GetNamedModule API
+ * @compile GetNamedModuleTest.java
+ * @run main/othervm/native -agentlib:GetNamedModuleTest MyPackage.GetNamedModuleTest
+ */
+
+import java.io.PrintStream;
+
+public class GetNamedModuleTest {
+
+ static {
+ try {
+ System.loadLibrary("GetNamedModuleTest");
+ } catch (UnsatisfiedLinkError ule) {
+ System.err.println("Could not load GetNamedModuleTest library");
+ System.err.println("java.library.path: "
+ + System.getProperty("java.library.path"));
+ throw ule;
+ }
+ }
+
+ native static int check();
+
+ public static void main(String args[]) {
+ int status = check();
+ if (status != 0) {
+ throw new RuntimeException("Non-zero status returned from the agent: " + status);
+ }
+ }
+}
diff --git a/hotspot/test/serviceability/jvmti/GetNamedModule/libGetNamedModuleTest.c b/hotspot/test/serviceability/jvmti/GetNamedModule/libGetNamedModuleTest.c
new file mode 100644
index 00000000000..6e9b0300232
--- /dev/null
+++ b/hotspot/test/serviceability/jvmti/GetNamedModule/libGetNamedModuleTest.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include
+#include
+#include "jvmti.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef JNI_ENV_ARG
+
+#ifdef __cplusplus
+#define JNI_ENV_ARG(x, y) y
+#define JNI_ENV_PTR(x) x
+#else
+#define JNI_ENV_ARG(x,y) x, y
+#define JNI_ENV_PTR(x) (*x)
+#endif
+
+#endif
+
+#define TranslateError(err) "JVMTI error"
+
+#define PASSED 0
+#define FAILED 2
+
+static const char *EXC_CNAME = "java/lang/Exception";
+static const char* MOD_CNAME = "Ljava/lang/reflect/Module;";
+
+static jvmtiEnv *jvmti = NULL;
+static jint result = PASSED;
+static jboolean printdump = JNI_FALSE;
+
+static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
+
+JNIEXPORT
+jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+ return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
+ return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
+ return JNI_VERSION_1_8;
+}
+
+static
+jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+ jint res;
+
+ if (options != NULL && strcmp(options, "printdump") == 0) {
+ printdump = JNI_TRUE;
+ }
+
+ res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
+ JVMTI_VERSION_9);
+ if (res != JNI_OK || jvmti == NULL) {
+ printf(" Error: wrong result of a valid call to GetEnv!\n");
+ return JNI_ERR;
+ }
+
+ return JNI_OK;
+}
+
+static
+jint throw_exc(JNIEnv *env, char *msg) {
+ jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
+
+ if (exc_class == NULL) {
+ printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME);
+ return -1;
+ }
+ return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
+}
+
+static
+jobject get_class_loader(jclass cls) {
+ jvmtiError err = JVMTI_ERROR_NONE;
+ jobject loader = NULL;
+
+ if (printdump == JNI_TRUE) {
+ printf(">>> getting class loader ...\n");
+ }
+ err = (*jvmti)->GetClassLoader(jvmti, cls, &loader);
+ if (err != JVMTI_ERROR_NONE) {
+ printf(" Error in GetClassLoader: %s (%d)\n", TranslateError(err), err);
+ }
+ return loader;
+}
+
+static
+jclass jlrM(JNIEnv *env) {
+ jclass cls = NULL;
+
+ cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, MOD_CNAME));
+ if (cls == NULL) {
+ printf(" Error in JNI FindClass: %s\n", MOD_CNAME);
+ }
+ return cls;
+}
+
+jmethodID
+get_method(JNIEnv *env, jclass clazz, const char * name, const char *sig) {
+ jmethodID method = NULL;
+
+ method = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, clazz), name, sig);
+ if (method == NULL) {
+ printf(" Error in JNI GetMethodID %s with signature %s", name, sig);
+ }
+ return method;
+}
+
+static
+jobject get_module_loader(JNIEnv *env, jobject module) {
+ static jmethodID cl_method = NULL;
+ jobject loader = NULL;
+
+ if (cl_method == NULL) {
+ cl_method = get_method(env, jlrM(env), "getClassLoader", "()Ljava/lang/ClassLoader;");
+ }
+ loader = (jobject)JNI_ENV_PTR(env)->CallObjectMethod(JNI_ENV_ARG(env, module), cl_method);
+ return loader;
+}
+
+static
+const char* get_module_name(JNIEnv *env, jobject module) {
+ static jmethodID method = NULL;
+ jobject loader = NULL;
+ jstring jstr = NULL;
+ const char *name = NULL;
+ const char *nstr = NULL;
+
+ if (method == NULL) {
+ method = get_method(env, jlrM(env), "getName", "()Ljava/lang/String;");
+ }
+ jstr = (jstring)JNI_ENV_PTR(env)->CallObjectMethod(JNI_ENV_ARG(env, module), method);
+ if (jstr != NULL) {
+ name = JNI_ENV_PTR(env)->GetStringUTFChars(JNI_ENV_ARG(env, jstr), NULL);
+ }
+ loader = get_module_loader(env, module);
+ nstr = (name == NULL) ? "" : name;
+ printf(" loader: %p, module: %p, name: %s\n", loader, module, nstr);
+ return name;
+}
+
+static
+jvmtiError get_module(JNIEnv *env,
+ jobject loader,
+ const char* pkg_name,
+ jobject* module_ptr,
+ const char** mod_name_ptr) {
+ jvmtiError err = JVMTI_ERROR_NONE;
+ const char* name = (pkg_name == NULL) ? "" : pkg_name;
+
+ printf(">>> getting module by loader %p and package \"%s\"\n", loader, name);
+ *mod_name_ptr = NULL;
+ err = (*jvmti)->GetNamedModule(jvmti, loader, pkg_name, module_ptr);
+ if (err != JVMTI_ERROR_NONE) {
+ printf(" Error in GetNamedModule for package \"%s\": %s (%d)\n",
+ pkg_name, TranslateError(err), err);
+ return err;
+ }
+ printf(" returned module: %p\n", *module_ptr);
+ if (*module_ptr == NULL) { // named module was not found
+ return err;
+ }
+ *mod_name_ptr = get_module_name(env, *module_ptr);
+ return err;
+}
+
+static
+jint get_all_modules(JNIEnv *env) {
+ jvmtiError err;
+ jint cnt = -1;
+ jint idx = 0;
+ jobject* modules;
+
+ printf(">>> Inspecting modules with GetAllModules\n");
+ err = (*jvmti)->GetAllModules(jvmti, &cnt, &modules);
+ if (err != JVMTI_ERROR_NONE) {
+ printf("Error in GetAllModules: %d\n", err);
+ return -1;
+ }
+ for (idx = 0; idx < cnt; ++idx) {
+ get_module_name(env, modules[idx]);
+ }
+ return cnt;
+}
+
+static
+jint check_bad_loader(JNIEnv *env, jobject loader) {
+ jvmtiError err = JVMTI_ERROR_NONE;
+ jobject module = NULL;
+ const char* mod_name = NULL;
+
+ err = get_module(env, loader, "", &module, &mod_name);
+ if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
+ return FAILED;
+ }
+ printf(" got expected JVMTI_ERROR_ILLEGAL_ARGUMENT for bad loader\n");
+ return PASSED;
+}
+
+static
+jint check_system_loader(JNIEnv *env, jobject loader) {
+ jvmtiError err = JVMTI_ERROR_NONE;
+ jobject module = NULL;
+ const char* exp_name = NULL;
+ const char* mod_name = NULL;
+
+ // NULL pointer for package name
+ err = get_module(env, loader, NULL, &module, &mod_name);
+ if (err != JVMTI_ERROR_NULL_POINTER) {
+ throw_exc(env, "check #SN1: failed to return JVMTI_ERROR_NULL_POINTER for NULL package");
+ return FAILED;
+ }
+
+ // NULL pointer for module_ptr
+ err = (*jvmti)->GetNamedModule(jvmti, loader, "", NULL);
+ if (err != JVMTI_ERROR_NULL_POINTER) {
+ throw_exc(env, "check #SN2: failed to return JVMTI_ERROR_NULL_POINTER for NULL module_ptr");
+ return FAILED;
+ }
+
+ // Unnamed/default package ""
+ err = get_module(env, loader, "", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #S1: failed to return JVMTI_ERROR_NONE for default package");
+ return FAILED;
+ }
+ if (module != NULL || mod_name != NULL) {
+ throw_exc(env, "check #S2: failed to return NULL-module for default package");
+ return FAILED;
+ }
+
+ // Test package: MyPackage
+ err = get_module(env, loader, "MyPackage", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #S3: failed to return JVMTI_ERROR_NONE for MyPackage");
+ return FAILED;
+ }
+ if (module != NULL || mod_name != NULL) {
+ throw_exc(env, "check #S4: failed to return NULL-module for MyPackage");
+ return FAILED;
+ }
+
+ // Package: com/sun/jdi
+ exp_name = "jdk.jdi";
+ err = get_module(env, loader, "com/sun/jdi", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #S5: failed to return JVMTI_ERROR_NONE for test package");
+ return FAILED;
+ }
+ if (module == NULL || mod_name == NULL) {
+ throw_exc(env, "check #S6: failed to return named module for com/sun/jdi package");
+ return FAILED;
+ }
+ if (strcmp(mod_name, exp_name) != 0) {
+ printf("check #S7: failed to return right module, expected: %s, returned: %s\n",
+ exp_name, mod_name);
+ throw_exc(env, "check #S7: failed to return jdk.jdi module for com/sun/jdi package");
+ return FAILED;
+ }
+
+ // Non-existing package: "bad/package/name"
+ err = get_module(env, loader, "bad/package/name", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #S8: failed to return JVMTI_ERROR_NONE for bad package");
+ return FAILED;
+ }
+ if (module != NULL || mod_name != NULL) {
+ throw_exc(env, "check #S9: failed to return NULL-module for bad package");
+ return FAILED;
+ }
+ return PASSED;
+}
+
+static
+jint check_bootstrap_loader(JNIEnv *env, jobject loader) {
+ jvmtiError err = JVMTI_ERROR_NONE;
+ jobject module = NULL;
+ const char* exp_name = NULL;
+ const char* mod_name = NULL;
+
+ // NULL pointer for package name
+ err = get_module(env, loader, NULL, &module, &mod_name);
+ if (err != JVMTI_ERROR_NULL_POINTER) {
+ throw_exc(env, "check #BN1: failed to return JVMTI_ERROR_NULL_POINTER for NULL package");
+ return FAILED;
+ }
+
+ // NULL pointer for module_ptr
+ err = (*jvmti)->GetNamedModule(jvmti, loader, "", NULL);
+ if (err != JVMTI_ERROR_NULL_POINTER) {
+ throw_exc(env, "check #BN2: failed to return JVMTI_ERROR_NULL_POINTER for NULL module_ptr");
+ return FAILED;
+ }
+
+ // Unnamed/default package ""
+ err = get_module(env, loader, "", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #B1: failed to return JVMTI_ERROR_NONE for default package");
+ return FAILED;
+ }
+ if (module != NULL || mod_name != NULL) {
+ throw_exc(env, "check #B2: failed to return NULL-module for default package");
+ return FAILED;
+ }
+
+ // Normal package from java.base module: "java/lang"
+ exp_name = "java.base";
+ err = get_module(env, loader, "java/lang", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #B3: failed to return JVMTI_ERROR_NONE for java/lang package");
+ return FAILED;
+ }
+ if (module == NULL || mod_name == NULL) {
+ throw_exc(env, "check #B4: failed to return named module for java/lang package");
+ return FAILED;
+ }
+ if (strcmp(exp_name, mod_name) != 0) {
+ printf("check #B5: failed to return right module, expected: %s, returned: %s\n",
+ exp_name, mod_name);
+ throw_exc(env, "check #B5: failed to return expected module for java/lang package");
+ return FAILED;
+ }
+
+ // Non-existing package: "bad/package/name"
+ err = get_module(env, loader, "bad/package/name", &module, &mod_name);
+ if (err != JVMTI_ERROR_NONE) {
+ throw_exc(env, "check #B6: failed to return JVMTI_ERROR_NONE for bad package");
+ return FAILED;
+ }
+ if (module != NULL || mod_name != NULL) {
+ throw_exc(env, "check #B7: failed to return NULL-module for bad package");
+ return FAILED;
+ }
+ return PASSED;
+}
+
+JNIEXPORT jint JNICALL
+Java_MyPackage_GetNamedModuleTest_check(JNIEnv *env, jclass cls) {
+ jobject loader = NULL;
+
+ if (jvmti == NULL) {
+ throw_exc(env, "JVMTI client was not properly loaded!\n");
+ return FAILED;
+ }
+
+ get_all_modules(env);
+
+ printf("\n*** Check for bad ClassLoader ***\n\n");
+ result = check_bad_loader(env, (jobject)cls);
+ if (result != PASSED) {
+ throw_exc(env, "check #L1: failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT for bad loader");
+ return result;
+ }
+
+ loader = get_class_loader(cls);
+ if (loader == NULL) {
+ throw_exc(env, "check #L2: failed to return non-NULL loader for valid test class");
+ return FAILED;
+ }
+
+ printf("\n*** Checks for System ClassLoader ***\n\n");
+ result = check_system_loader(env, loader);
+ if (result != PASSED) {
+ return result;
+ }
+
+ printf("\n*** Checks for Bootstrap ClassLoader ***\n\n");
+ result = check_bootstrap_loader(env, NULL);
+
+ return result;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hotspot/test/serviceability/sa/TestClassLoaderStats.java b/hotspot/test/serviceability/sa/TestClassLoaderStats.java
index df46c4136fc..6cbd9ee42d6 100644
--- a/hotspot/test/serviceability/sa/TestClassLoaderStats.java
+++ b/hotspot/test/serviceability/sa/TestClassLoaderStats.java
@@ -32,6 +32,9 @@ import jdk.test.lib.apps.LingeredApp;
/*
* @test
+ * @summary Started failing on 2016.06.24 due to 8160376 on MacOS X so
+ * quarantine it on that platform:
+ * @requires os.family != "mac"
* @modules java.base/jdk.internal.misc
* @library /test/lib/share/classes
* @library /testlibrary
diff --git a/hotspot/test/serviceability/sa/TestStackTrace.java b/hotspot/test/serviceability/sa/TestStackTrace.java
index 7044f104f65..de6c9708dbb 100644
--- a/hotspot/test/serviceability/sa/TestStackTrace.java
+++ b/hotspot/test/serviceability/sa/TestStackTrace.java
@@ -32,6 +32,9 @@ import jdk.test.lib.apps.LingeredApp;
/*
* @test
+ * @summary Started failing on 2016.06.24 due to 8160376 on MacOS X so
+ * quarantine it on that platform:
+ * @requires os.family != "mac"
* @modules java.base/jdk.internal.misc
* @library /test/lib/share/classes
* @library /testlibrary
diff --git a/hotspot/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java b/hotspot/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java
index 077617b2fb0..459686ee71c 100644
--- a/hotspot/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java
+++ b/hotspot/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java
@@ -43,6 +43,9 @@ import jdk.test.lib.ProcessTools;
* @bug 6313383
* @key regression
* @summary Regression test for hprof export issue due to large heaps (>2G)
+ * Started failing on 2016.06.24 due to 8160376 on MacOS X so quarantine
+ * it on that platform:
+ * @requires os.family != "mac"
* @library /testlibrary
* @modules java.base/jdk.internal.misc
* java.compiler
diff --git a/jdk/.hgtags b/jdk/.hgtags
index f2f2ce61f7e..b835fba9fa3 100644
--- a/jdk/.hgtags
+++ b/jdk/.hgtags
@@ -370,3 +370,4 @@ c40c8739bcdc88892ff58ebee3fd8a3f287be94d jdk-9+123
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127
+9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128
diff --git a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
index 505ca0814dc..bcadaac2359 100644
--- a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
+++ b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.lang;
import java.io.File;
diff --git a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
index ea3dc25c624..b43f290b0a2 100644
--- a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
+++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
@@ -177,8 +177,14 @@ void setOSNameAndVersion(java_props_t *sprops) {
OSVerStruct (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret;
OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo],
@selector(operatingSystemVersion));
- NSString *nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
- (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ NSString *nsVerStr;
+ if (osVer.patchVersion == 0) { // Omit trailing ".0"
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion];
+ } else {
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ }
// Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]);
}
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
index 32cb3e39d3d..9d11f0091df 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -172,6 +172,11 @@ abstract class AESCipher extends CipherSpi {
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
+ /*
+ * needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
+ */
+ private boolean updateCalled;
+
/**
* Creates an instance of AES cipher with default ECB mode and
* PKCS5Padding.
@@ -304,6 +309,7 @@ abstract class AESCipher extends CipherSpi {
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, random);
}
@@ -336,6 +342,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -344,6 +351,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -368,6 +376,7 @@ abstract class AESCipher extends CipherSpi {
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen);
}
@@ -397,6 +406,7 @@ abstract class AESCipher extends CipherSpi {
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
@@ -433,7 +443,9 @@ abstract class AESCipher extends CipherSpi {
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen);
+ byte[] out = core.doFinal(input, inputOffset, inputLen);
+ updateCalled = false;
+ return out;
}
/**
@@ -476,8 +488,10 @@ abstract class AESCipher extends CipherSpi {
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen, output,
- outputOffset);
+ int outLen = core.doFinal(input, inputOffset, inputLen, output,
+ outputOffset);
+ updateCalled = false;
+ return outLen;
}
/**
@@ -574,6 +588,9 @@ abstract class AESCipher extends CipherSpi {
*/
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
core.updateAAD(src, offset, len);
}
@@ -606,6 +623,9 @@ abstract class AESCipher extends CipherSpi {
*/
@Override
protected void engineUpdateAAD(ByteBuffer src) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
if (src != null) {
int aadLen = src.limit() - src.position();
if (aadLen != 0) {
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
index 4cdfdf8850e..592ec411407 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -124,7 +124,7 @@ final class CipherCore {
private static final int PCBC_MODE = 4;
private static final int CTR_MODE = 5;
private static final int CTS_MODE = 6;
- private static final int GCM_MODE = 7;
+ static final int GCM_MODE = 7;
/*
* variables used for performing the GCM (key+iv) uniqueness check.
@@ -196,7 +196,7 @@ final class CipherCore {
cipher = new CounterMode(rawImpl);
unitBytes = 1;
padding = null;
- } else if (modeUpperCase.startsWith("GCM")) {
+ } else if (modeUpperCase.equals("GCM")) {
// can only be used for block ciphers w/ 128-bit block size
if (blockSize != 16) {
throw new NoSuchAlgorithmException
@@ -223,6 +223,15 @@ final class CipherCore {
}
}
+ /**
+ * Returns the mode of this cipher.
+ *
+ * @return the parsed cipher mode
+ */
+ int getMode() {
+ return cipherMode;
+ }
+
private static int getNumOfUnit(String mode, int offset, int blockSize)
throws NoSuchAlgorithmException {
int result = blockSize; // use blockSize as default value
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
index 87fa4a383f3..7dd9791f1b1 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,16 @@ final class GaloisCounterMode extends FeedbackCipher {
static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes
+ // In NIST SP 800-38D, GCM input size is limited to be no longer
+ // than (2^36 - 32) bytes. Otherwise, the counter will wrap
+ // around and lead to a leak of plaintext.
+ // However, given the current GCM spec requirement that recovered
+ // text can only be returned after successful tag verification,
+ // we are bound by limiting the data size to the size limit of
+ // java byte array, e.g. Integer.MAX_VALUE, since all data
+ // can only be returned by the doFinal(...) call.
+ private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
+
// buffer for AAD data; if null, meaning update has been called
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0;
@@ -89,9 +99,13 @@ final class GaloisCounterMode extends FeedbackCipher {
}
}
- // ivLen in bits
- private static byte[] getLengthBlock(int ivLen) {
+ private static byte[] getLengthBlock(int ivLenInBytes) {
+ long ivLen = ((long)ivLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[8] = (byte)(ivLen >>> 56);
+ out[9] = (byte)(ivLen >>> 48);
+ out[10] = (byte)(ivLen >>> 40);
+ out[11] = (byte)(ivLen >>> 32);
out[12] = (byte)(ivLen >>> 24);
out[13] = (byte)(ivLen >>> 16);
out[14] = (byte)(ivLen >>> 8);
@@ -99,13 +113,22 @@ final class GaloisCounterMode extends FeedbackCipher {
return out;
}
- // aLen and cLen both in bits
- private static byte[] getLengthBlock(int aLen, int cLen) {
+ private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
+ long aLen = ((long)aLenInBytes) << 3;
+ long cLen = ((long)cLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[0] = (byte)(aLen >>> 56);
+ out[1] = (byte)(aLen >>> 48);
+ out[2] = (byte)(aLen >>> 40);
+ out[3] = (byte)(aLen >>> 32);
out[4] = (byte)(aLen >>> 24);
out[5] = (byte)(aLen >>> 16);
out[6] = (byte)(aLen >>> 8);
out[7] = (byte)aLen;
+ out[8] = (byte)(cLen >>> 56);
+ out[9] = (byte)(cLen >>> 48);
+ out[10] = (byte)(cLen >>> 40);
+ out[11] = (byte)(cLen >>> 32);
out[12] = (byte)(cLen >>> 24);
out[13] = (byte)(cLen >>> 16);
out[14] = (byte)(cLen >>> 8);
@@ -142,13 +165,20 @@ final class GaloisCounterMode extends FeedbackCipher {
} else {
g.update(iv);
}
- byte[] lengthBlock = getLengthBlock(iv.length*8);
+ byte[] lengthBlock = getLengthBlock(iv.length);
g.update(lengthBlock);
j0 = g.digest();
}
return j0;
}
+ private static void checkDataLength(int processed, int len) {
+ if (processed > MAX_BUF_SIZE - len) {
+ throw new ProviderException("SunJCE provider only supports " +
+ "input size up to " + MAX_BUF_SIZE + " bytes");
+ }
+ }
+
GaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream();
@@ -319,20 +349,22 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
- if (aadBuffer != null && aadBuffer.size() > 0) {
- byte[] aad = aadBuffer.toByteArray();
- sizeOfAAD = aad.length;
- aadBuffer = null;
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ byte[] aad = aadBuffer.toByteArray();
+ sizeOfAAD = aad.length;
- int lastLen = aad.length % AES_BLOCK_SIZE;
- if (lastLen != 0) {
- ghashAllToS.update(aad, 0, aad.length - lastLen);
- byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
- lastLen);
- ghashAllToS.update(padded);
- } else {
- ghashAllToS.update(aad);
+ int lastLen = aad.length % AES_BLOCK_SIZE;
+ if (lastLen != 0) {
+ ghashAllToS.update(aad, 0, aad.length - lastLen);
+ byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
+ lastLen);
+ ghashAllToS.update(padded);
+ } else {
+ ghashAllToS.update(aad);
+ }
}
+ aadBuffer = null;
}
}
@@ -384,6 +416,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs);
@@ -405,17 +440,23 @@ final class GaloisCounterMode extends FeedbackCipher {
*/
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException {
+ if (len > MAX_BUF_SIZE - tagLenBytes) {
+ throw new ShortBufferException
+ ("Can't fit both data and tag into one buffer");
+ }
if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, true);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length];
@@ -447,6 +488,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(ibuffer.size(), len);
+
processAAD();
if (len > 0) {
@@ -481,10 +525,21 @@ final class GaloisCounterMode extends FeedbackCipher {
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
+ // do this check here can also catch the potential integer overflow
+ // scenario for the subsequent output buffer capacity check.
+ checkDataLength(ibuffer.size(), (len - tagLenBytes));
+
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+
processAAD();
+
+ // get the trailing tag bytes from 'in'
+ byte[] tag = new byte[tagLenBytes];
+ System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
+ len -= tagLenBytes;
+
if (len != 0) {
ibuffer.write(in, inOfs, len);
}
@@ -495,17 +550,12 @@ final class GaloisCounterMode extends FeedbackCipher {
len = in.length;
ibuffer.reset();
- byte[] tag = new byte[tagLenBytes];
- // get the trailing tag bytes from 'in'
- System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
- len -= tagLenBytes;
-
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, false);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
diff --git a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
index 2e97579e669..c826d82f990 100644
--- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
+++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java
index 4649b04b135..7ac3d50202c 100644
--- a/jdk/src/java.base/share/classes/java/lang/Class.java
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java
@@ -238,15 +238,11 @@ public final class Class implements java.io.Serializable,
TypeVariable>[] typeparms = component.getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", ">");
for(TypeVariable> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- sb.append(typeparm.getTypeName());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append('>');
+ sb.append(sj.toString());
}
for (int i = 0; i < arrayDepth; i++)
diff --git a/jdk/src/java.base/share/classes/java/lang/Runtime.java b/jdk/src/java.base/share/classes/java/lang/Runtime.java
index fd5cb88a7d6..8cac75b4826 100644
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java
+++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java
@@ -945,7 +945,7 @@ public class Runtime {
}
/**
- * A representation of a version string for an implemenation of the
+ * A representation of a version string for an implementation of the
* Java SE Platform. A version string contains a version number
* optionally followed by pre-release and build information.
*
@@ -1058,10 +1058,10 @@ public class Runtime {
* When comparing two version strings, the value of {@code $OPT}, if
* present, may or may not be significant depending on the chosen
* comparison method. The comparison methods {@link #compareTo(Version)
- * compareTo()} and {@link #compareToIgnoreOpt(Version)
- * compareToIgnoreOpt()} should be used consistently with the
+ * compareTo()} and {@link #compareToIgnoreOptional(Version)
+ * compareToIgnoreOptional()} should be used consistently with the
* corresponding methods {@link #equals(Object) equals()} and {@link
- * #equalsIgnoreOpt(Object) equalsIgnoreOpt()}.
+ * #equalsIgnoreOptional(Object) equalsIgnoreOptional()}.
*
* A short version string, {@code $SVSTR}, often useful in
* less formal contexts, is a version number optionally followed by a
@@ -1249,7 +1249,7 @@ public class Runtime {
* @throws NullPointerException
* If the given object is {@code null}
*/
- public int compareToIgnoreOpt(Version ob) {
+ public int compareToIgnoreOptional(Version ob) {
return compare(ob, true);
}
@@ -1270,7 +1270,7 @@ public class Runtime {
return ret;
if (!ignoreOpt)
- return compareOpt(ob);
+ return compareOptional(ob);
return 0;
}
@@ -1325,7 +1325,7 @@ public class Runtime {
return 0;
}
- private int compareOpt(Version ob) {
+ private int compareOptional(Version ob) {
Optional oOpt = ob.optional();
if (!optional.isPresent()) {
if (oOpt.isPresent())
@@ -1384,7 +1384,7 @@ public class Runtime {
*/
@Override
public boolean equals(Object ob) {
- boolean ret = equalsIgnoreOpt(ob);
+ boolean ret = equalsIgnoreOptional(ob);
if (!ret)
return false;
@@ -1407,7 +1407,7 @@ public class Runtime {
* ignoring the optinal build information
*
*/
- public boolean equalsIgnoreOpt(Object ob) {
+ public boolean equalsIgnoreOptional(Object ob) {
if (this == ob)
return true;
if (!(ob instanceof Version))
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
index f9c864a1ac1..fe77e01fcc7 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -155,7 +155,7 @@ class DirectMethodHandle extends MethodHandle {
private static LambdaForm preparedLambdaForm(MemberName m) {
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
MethodType mtype = m.getInvocationType().basicType();
- assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+ assert(!m.isMethodHandleInvoke()) : m;
int which;
switch (m.getReferenceKind()) {
case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
index a0a04559ff9..2a722db28e0 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
@@ -1049,7 +1049,7 @@ class LambdaForm {
this.member = member;
this.resolvedHandle = resolvedHandle;
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
- //assert(!isInvokeBasic());
+ //assert(!isInvokeBasic(member));
}
NamedFunction(MethodType basicInvokerType) {
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
@@ -1060,13 +1060,13 @@ class LambdaForm {
// necessary to pass BigArityTest
this.member = Invokers.invokeBasicMethod(basicInvokerType);
}
- assert(isInvokeBasic());
+ assert(isInvokeBasic(member));
}
- private boolean isInvokeBasic() {
+ private static boolean isInvokeBasic(MemberName member) {
return member != null &&
- member.isMethodHandleInvoke() &&
- "invokeBasic".equals(member.getName());
+ member.getDeclaringClass() == MethodHandle.class &&
+ "invokeBasic".equals(member.getName());
}
// The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
@@ -1204,7 +1204,7 @@ class LambdaForm {
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
: Arrays.asList(mh, rtype, arity);
MemberName member = mh.internalMemberName();
- if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
+ if (isInvokeBasic(member)) {
assert(arity > 0);
assert(a[0] instanceof MethodHandle);
MethodHandle mh2 = (MethodHandle) a[0];
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
index 776d5266db3..3b56095d438 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
@@ -346,7 +346,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
}
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
- * Also returns true for the non-public MH.invokeBasic.
*/
public boolean isMethodHandleInvoke() {
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
@@ -361,7 +360,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
switch (name) {
case "invoke":
case "invokeExact":
- case "invokeBasic": // internal sig-poly method
return true;
default:
return false;
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 1150c188b8c..14f7d32e2e2 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -951,8 +951,6 @@ assertEquals("", (String) MH_newString.invokeExact());
return invoker(type);
if ("invokeExact".equals(name))
return exactInvoker(type);
- if ("invokeBasic".equals(name))
- return basicInvoker(type);
assert(!MemberName.isMethodHandleInvokeName(name));
return null;
}
@@ -3268,6 +3266,16 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, List> valueTypes) {
+ return dropArguments0(target, pos, copyTypes(valueTypes));
+ }
+
+ private static List> copyTypes(List> types) {
+ Object[] a = types.toArray();
+ return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
+ }
+
+ private static
+ MethodHandle dropArguments0(MethodHandle target, int pos, List> valueTypes) {
MethodType oldType = target.type(); // get NPE
int dropped = dropArgumentChecks(oldType, pos, valueTypes);
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
@@ -3348,6 +3356,7 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// private version which allows caller some freedom with error handling
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List> newTypes, int pos,
boolean nullOnFailure) {
+ newTypes = copyTypes(newTypes);
List> oldTypes = target.type().parameterList();
int match = oldTypes.size();
if (skip != 0) {
@@ -3379,11 +3388,11 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// target: ( S*[skip], M*[match] )
MethodHandle adapter = target;
if (add > 0) {
- adapter = dropArguments(adapter, skip+ match, addTypes);
+ adapter = dropArguments0(adapter, skip+ match, addTypes);
}
// adapter: (S*[skip], M*[match], A*[add] )
if (pos > 0) {
- adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
+ adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
}
// adapter: (S*[skip], P*[pos], M*[match], A*[add] )
return adapter;
@@ -3787,7 +3796,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
int filterValues = filterType.parameterCount();
if (filterValues == 0
? (rtype != void.class)
- : (rtype != filterType.parameterType(0)))
+ : (rtype != filterType.parameterType(0) || filterValues != 1))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
}
@@ -4290,7 +4299,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
}
if (pred.get(i) == null) {
- pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
+ pred.set(i, dropArguments0(constant(boolean.class, true), 0, commonParameterSequence));
}
if (fini.get(i) == null) {
fini.set(i, empty(methodType(t, commonParameterSequence)));
@@ -4315,7 +4324,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return hs.stream().map(h -> {
int pc = h.type().parameterCount();
int tpsize = targetParams.size();
- return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+ return pc < tpsize ? dropArguments0(h, pc, targetParams.subList(pc, tpsize)) : h;
}).collect(Collectors.toList());
}
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
index 92c16bdfbc2..d300bc007e3 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
@@ -52,6 +52,7 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -420,7 +421,7 @@ class ModulePath implements ConfigurableModuleFinder {
// scan the entries in the JAR file to locate the .class and service
// configuration file
Map> map =
- jf.stream()
+ versionedStream(jf)
.map(JarEntry::getName)
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
@@ -503,8 +504,21 @@ class ModulePath implements ConfigurableModuleFinder {
return mn;
}
+ private Stream versionedStream(JarFile jf) {
+ if (jf.isMultiRelease()) {
+ // a stream of JarEntries whose names are base names and whose
+ // contents are from the corresponding versioned entries in
+ // a multi-release jar file
+ return jf.stream().map(JarEntry::getName)
+ .filter(name -> !name.startsWith("META-INF/versions/"))
+ .map(jf::getJarEntry);
+ } else {
+ return jf.stream();
+ }
+ }
+
private Set jarPackages(JarFile jf) {
- return jf.stream()
+ return versionedStream(jf)
.filter(e -> e.getName().endsWith(".class"))
.map(e -> toPackageName(e.getName()))
.filter(pkg -> pkg.length() > 0) // module-info
diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
index 88a9f5de9ac..812e22816f1 100644
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@ package java.lang.reflect;
import java.lang.annotation.*;
import java.util.Map;
import java.util.Objects;
+import java.util.StringJoiner;
import jdk.internal.misc.SharedSecrets;
import sun.reflect.annotation.AnnotationParser;
@@ -86,15 +87,6 @@ public abstract class Executable extends AccessibleObject
getDeclaringClass());
}
- void separateWithCommas(Class>[] types, StringBuilder sb) {
- for (int j = 0; j < types.length; j++) {
- sb.append(types[j].getTypeName());
- if (j < (types.length - 1))
- sb.append(",");
- }
-
- }
-
void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
int mod = getModifiers() & mask;
@@ -121,13 +113,20 @@ public abstract class Executable extends AccessibleObject
printModifiersIfNonzero(sb, modifierMask, isDefault);
specificToStringHeader(sb);
-
sb.append('(');
- separateWithCommas(parameterTypes, sb);
+ StringJoiner sj = new StringJoiner(",");
+ for (Class> parameterType : parameterTypes) {
+ sj.add(parameterType.getTypeName());
+ }
+ sb.append(sj.toString());
sb.append(')');
+
if (exceptionTypes.length > 0) {
- sb.append(" throws ");
- separateWithCommas(exceptionTypes, sb);
+ StringJoiner joiner = new StringJoiner(",", "throws ", "");
+ for (Class> exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
+ }
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
@@ -149,42 +148,34 @@ public abstract class Executable extends AccessibleObject
TypeVariable>[] typeparms = getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", "> ");
for(TypeVariable> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- // Class objects can't occur here; no need to test
- // and call Class.getName().
- sb.append(typeparm.toString());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append("> ");
+ sb.append(sj.toString());
}
specificToGenericStringHeader(sb);
sb.append('(');
+ StringJoiner sj = new StringJoiner(",");
Type[] params = getGenericParameterTypes();
for (int j = 0; j < params.length; j++) {
String param = params[j].getTypeName();
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
param = param.replaceFirst("\\[\\]$", "...");
- sb.append(param);
- if (j < (params.length - 1))
- sb.append(',');
+ sj.add(param);
}
+ sb.append(sj.toString());
sb.append(')');
- Type[] exceptions = getGenericExceptionTypes();
- if (exceptions.length > 0) {
- sb.append(" throws ");
- for (int k = 0; k < exceptions.length; k++) {
- sb.append((exceptions[k] instanceof Class)?
- ((Class)exceptions[k]).getName():
- exceptions[k].toString());
- if (k < (exceptions.length - 1))
- sb.append(',');
+
+ Type[] exceptionTypes = getGenericExceptionTypes();
+ if (exceptionTypes.length > 0) {
+ StringJoiner joiner = new StringJoiner(",", " throws ", "");
+ for (Type exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
}
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
diff --git a/jdk/src/java.base/share/classes/java/net/URLPermission.java b/jdk/src/java.base/share/classes/java/net/URLPermission.java
index 8b2e8a8b536..0c38008cb13 100644
--- a/jdk/src/java.base/share/classes/java/net/URLPermission.java
+++ b/jdk/src/java.base/share/classes/java/net/URLPermission.java
@@ -461,11 +461,10 @@ public final class URLPermission extends Permission {
}
private String actions() {
- String b = String.join(",", methods);
- if (!requestHeaders.isEmpty()) {
- b += ":" + String.join(",", requestHeaders);
- }
- return b;
+ // The colon separator is optional when the request headers list is
+ // empty.This implementation chooses to include it even when the request
+ // headers list is empty.
+ return String.join(",", methods) + ":" + String.join(",", requestHeaders);
}
/**
diff --git a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
index 32cb2186a2e..fcd6f0be465 100644
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
@@ -132,7 +132,7 @@ public class ProtectionDomain {
/* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */
- private boolean staticPermissions;
+ private final boolean staticPermissions;
/*
* An object used as a key when the ProtectionDomain is stored in a Map.
@@ -143,8 +143,12 @@ public class ProtectionDomain {
* Creates a new ProtectionDomain with the given CodeSource and
* Permissions. If the permissions object is not null, then
* {@code setReadOnly()} will be called on the passed in
- * Permissions object. The only permissions granted to this domain
- * are the ones specified; the current Policy will not be consulted.
+ * Permissions object.
+ *
+ * The permissions granted to this domain are static, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns true.
+ * They contain only the ones passed to this constructor and
+ * the current Policy will not be consulted.
*
* @param codesource the codesource associated with this domain
* @param permissions the permissions granted to this domain
@@ -170,9 +174,11 @@ public class ProtectionDomain {
* Permissions, ClassLoader and array of Principals. If the
* permissions object is not null, then {@code setReadOnly()}
* will be called on the passed in Permissions object.
- * The permissions granted to this domain are dynamic; they include
- * both the static permissions passed to this constructor, and any
- * permissions granted to this domain by the current Policy at the
+ *
+ * The permissions granted to this domain are dynamic, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns false.
+ * They include both the static permissions passed to this constructor,
+ * and any permissions granted to this domain by the current Policy at the
* time a permission is checked.
*
* This constructor is typically used by
@@ -255,6 +261,19 @@ public class ProtectionDomain {
return permissions;
}
+ /**
+ * Returns true if this domain contains only static permissions
+ * and does not check the current {@code Policy} at the time of
+ * permission checking.
+ *
+ * @return true if this domain contains only static permissions.
+ *
+ * @since 9
+ */
+ public final boolean staticPermissionsOnly() {
+ return this.staticPermissions;
+ }
+
/**
* Check and see if this ProtectionDomain implies the permissions
* expressed in the Permission object.
@@ -263,25 +282,19 @@ public class ProtectionDomain {
* ProtectionDomain was constructed with a static set of permissions
* or it was bound to a dynamically mapped set of permissions.
*
- * If the ProtectionDomain was constructed to a
- * {@link #ProtectionDomain(CodeSource, PermissionCollection)
- * statically bound} PermissionCollection then the permission will
- * only be checked against the PermissionCollection supplied at
- * construction.
+ * If the {@link #staticPermissionsOnly()} method returns
+ * true, then the permission will only be checked against the
+ * PermissionCollection supplied at construction.
*
- * However, if the ProtectionDomain was constructed with
- * the constructor variant which supports
- * {@link #ProtectionDomain(CodeSource, PermissionCollection,
- * ClassLoader, java.security.Principal[]) dynamically binding}
- * permissions, then the permission will be checked against the
- * combination of the PermissionCollection supplied at construction and
+ * Otherwise, the permission will be checked against the combination
+ * of the PermissionCollection supplied at construction and
* the current Policy binding.
*
- * @param permission the Permission object to check.
+ * @param perm the Permission object to check.
*
- * @return true if "permission" is implicit to this ProtectionDomain.
+ * @return true if {@code perm} is implied by this ProtectionDomain.
*/
- public boolean implies(Permission permission) {
+ public boolean implies(Permission perm) {
if (hasAllPerm) {
// internal permission collection already has AllPermission -
@@ -290,10 +303,10 @@ public class ProtectionDomain {
}
if (!staticPermissions &&
- Policy.getPolicyNoCheck().implies(this, permission))
+ Policy.getPolicyNoCheck().implies(this, perm))
return true;
if (permissions != null)
- return permissions.implies(permission);
+ return permissions.implies(perm);
return false;
}
diff --git a/jdk/src/java.base/share/classes/java/security/Provider.java b/jdk/src/java.base/share/classes/java/security/Provider.java
index de09dc49240..f9b0583df84 100644
--- a/jdk/src/java.base/share/classes/java/security/Provider.java
+++ b/jdk/src/java.base/share/classes/java/security/Provider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java
index 7d5e39c7030..e94b22c7fb2 100644
--- a/jdk/src/java.base/share/classes/java/util/Queue.java
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java
@@ -124,7 +124,6 @@ package java.util;
* always well-defined for queues with the same elements but different
* ordering properties.
*
- *
*
This interface is a member of the
*
* Java Collections Framework.
diff --git a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java
index 4ce2b50720a..bc7aaae063e 100644
--- a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java
+++ b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java
@@ -660,6 +660,7 @@ public abstract class ResourceBundle {
// ResourceBundleProviders for loading ResourceBundles
private ServiceLoader providers;
+ private boolean providersChecked;
// Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
private Boolean callerHasProvider;
@@ -675,7 +676,6 @@ public abstract class ResourceBundle {
this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
}
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
- this.providers = getServiceLoader(module, baseName);
calculateHashCode();
}
@@ -712,11 +712,15 @@ public abstract class ResourceBundle {
}
ServiceLoader getProviders() {
+ if (!providersChecked) {
+ providers = getServiceLoader(getModule(), name);
+ providersChecked = true;
+ }
return providers;
}
boolean hasProviders() {
- return providers != null;
+ return getProviders() != null;
}
boolean callerHasProvider() {
@@ -789,8 +793,9 @@ public abstract class ResourceBundle {
}
clone.moduleRef = new KeyElementReference<>(getModule(),
referenceQueue, clone);
- // Clear the reference to ResourceBundleProviders
+ // Clear the reference to ResourceBundleProviders and the flag
clone.providers = null;
+ clone.providersChecked = false;
// Clear the reference to a Throwable
clone.cause = null;
// Clear callerHasProvider
@@ -1841,6 +1846,9 @@ public abstract class ResourceBundle {
private static ServiceLoader getServiceLoader(Module module,
String baseName) {
+ if (!module.isNamed()) {
+ return null;
+ }
PrivilegedAction pa = module::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa);
return getServiceLoader(module, loader, baseName);
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
index a558cdad2f3..fd116b5343a 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -149,26 +151,29 @@ public class CompletableFuture implements Future, CompletionStage {
* applies across normal vs exceptional outcomes, sync vs async
* actions, binary triggers, and various forms of completions.
*
- * Non-nullness of field result (set via CAS) indicates done. An
- * AltResult is used to box null as a result, as well as to hold
- * exceptions. Using a single field makes completion simple to
- * detect and trigger. Encoding and decoding is straightforward
- * but adds to the sprawl of trapping and associating exceptions
- * with targets. Minor simplifications rely on (static) NIL (to
- * box null results) being the only AltResult with a null
- * exception field, so we don't usually need explicit comparisons.
- * Even though some of the generics casts are unchecked (see
- * SuppressWarnings annotations), they are placed to be
- * appropriate even if checked.
+ * Non-nullness of volatile field "result" indicates done. It may
+ * be set directly if known to be thread-confined, else via CAS.
+ * An AltResult is used to box null as a result, as well as to
+ * hold exceptions. Using a single field makes completion simple
+ * to detect and trigger. Result encoding and decoding is
+ * straightforward but tedious and adds to the sprawl of trapping
+ * and associating exceptions with targets. Minor simplifications
+ * rely on (static) NIL (to box null results) being the only
+ * AltResult with a null exception field, so we don't usually need
+ * explicit comparisons. Even though some of the generics casts
+ * are unchecked (see SuppressWarnings annotations), they are
+ * placed to be appropriate even if checked.
*
* Dependent actions are represented by Completion objects linked
* as Treiber stacks headed by field "stack". There are Completion
- * classes for each kind of action, grouped into single-input
- * (UniCompletion), two-input (BiCompletion), projected
- * (BiCompletions using either (not both) of two inputs), shared
- * (CoCompletion, used by the second of two sources), zero-input
- * source actions, and Signallers that unblock waiters. Class
- * Completion extends ForkJoinTask to enable async execution
+ * classes for each kind of action, grouped into:
+ * - single-input (UniCompletion),
+ * - two-input (BiCompletion),
+ * - projected (BiCompletions using exactly one of two inputs),
+ * - shared (CoCompletion, used by the second of two sources),
+ * - zero-input source actions,
+ * - Signallers that unblock waiters.
+ * Class Completion extends ForkJoinTask to enable async execution
* (adding no space overhead because we exploit its "tag" methods
* to maintain claims). It is also declared as Runnable to allow
* usage with arbitrary executors.
@@ -184,7 +189,7 @@ public class CompletableFuture implements Future, CompletionStage {
* encounter layers of adapters in common usages.
*
* * Boolean CompletableFuture method x(...) (for example
- * uniApply) takes all of the arguments needed to check that an
+ * biApply) takes all of the arguments needed to check that an
* action is triggerable, and then either runs the action or
* arranges its async execution by executing its Completion
* argument, if present. The method returns true if known to be
@@ -194,24 +199,32 @@ public class CompletableFuture implements Future, CompletionStage {
* method with its held arguments, and on success cleans up.
* The mode argument allows tryFire to be called twice (SYNC,
* then ASYNC); the first to screen and trap exceptions while
- * arranging to execute, and the second when called from a
- * task. (A few classes are not used async so take slightly
- * different forms.) The claim() callback suppresses function
- * invocation if already claimed by another thread.
+ * arranging to execute, and the second when called from a task.
+ * (A few classes are not used async so take slightly different
+ * forms.) The claim() callback suppresses function invocation
+ * if already claimed by another thread.
+ *
+ * * Some classes (for example UniApply) have separate handling
+ * code for when known to be thread-confined ("now" methods) and
+ * for when shared (in tryFire), for efficiency.
*
* * CompletableFuture method xStage(...) is called from a public
- * stage method of CompletableFuture x. It screens user
+ * stage method of CompletableFuture f. It screens user
* arguments and invokes and/or creates the stage object. If
- * not async and x is already complete, the action is run
- * immediately. Otherwise a Completion c is created, pushed to
- * x's stack (unless done), and started or triggered via
- * c.tryFire. This also covers races possible if x completes
- * while pushing. Classes with two inputs (for example BiApply)
- * deal with races across both while pushing actions. The
- * second completion is a CoCompletion pointing to the first,
- * shared so that at most one performs the action. The
- * multiple-arity methods allOf and anyOf do this pairwise to
- * form trees of completions.
+ * not async and already triggerable, the action is run
+ * immediately. Otherwise a Completion c is created, and
+ * submitted to the executor if triggerable, or pushed onto f's
+ * stack if not. Completion actions are started via c.tryFire.
+ * We recheck after pushing to a source future's stack to cover
+ * possible races if the source completes while pushing.
+ * Classes with two inputs (for example BiApply) deal with races
+ * across both while pushing actions. The second completion is
+ * a CoCompletion pointing to the first, shared so that at most
+ * one performs the action. The multiple-arity methods allOf
+ * does this pairwise to form trees of completions. Method
+ * anyOf is handled differently from allOf because completion of
+ * any source should trigger a cleanStack of other sources.
+ * Each AnyOf completion can reach others via a shared array.
*
* Note that the generic type parameters of methods vary according
* to whether "this" is a source, dependent, or completion.
@@ -236,29 +249,30 @@ public class CompletableFuture implements Future, CompletionStage {
* pointing back to its sources. So we null out fields as soon as
* possible. The screening checks needed anyway harmlessly ignore
* null arguments that may have been obtained during races with
- * threads nulling out fields. We also try to unlink fired
- * Completions from stacks that might never be popped (see method
- * postFire). Completion fields need not be declared as final or
- * volatile because they are only visible to other threads upon
- * safe publication.
+ * threads nulling out fields. We also try to unlink non-isLive
+ * (fired or cancelled) Completions from stacks that might
+ * otherwise never be popped: Method cleanStack always unlinks non
+ * isLive completions from the head of stack; others may
+ * occasionally remain if racing with other cancellations or
+ * removals.
+ *
+ * Completion fields need not be declared as final or volatile
+ * because they are only visible to other threads upon safe
+ * publication.
*/
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
final boolean internalComplete(Object r) { // CAS from null to r
- return U.compareAndSwapObject(this, RESULT, null, r);
- }
-
- final boolean casStack(Completion cmp, Completion val) {
- return U.compareAndSwapObject(this, STACK, cmp, val);
+ return RESULT.compareAndSet(this, null, r);
}
/** Returns true if successfully pushed c onto stack. */
final boolean tryPushStack(Completion c) {
Completion h = stack;
- lazySetNext(c, h);
- return U.compareAndSwapObject(this, STACK, h, c);
+ NEXT.set(c, h); // CAS piggyback
+ return STACK.compareAndSet(this, h, c);
}
/** Unconditionally pushes c onto stack, retrying if necessary. */
@@ -278,8 +292,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with the null value, unless already completed. */
final boolean completeNull() {
- return U.compareAndSwapObject(this, RESULT, null,
- NIL);
+ return RESULT.compareAndSet(this, null, NIL);
}
/** Returns the encoding of the given non-exceptional value. */
@@ -289,8 +302,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with a non-exceptional result, unless already completed. */
final boolean completeValue(T t) {
- return U.compareAndSwapObject(this, RESULT, null,
- (t == null) ? NIL : t);
+ return RESULT.compareAndSet(this, null, (t == null) ? NIL : t);
}
/**
@@ -304,8 +316,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with an exceptional result, unless already completed. */
final boolean completeThrowable(Throwable x) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x));
}
/**
@@ -332,8 +343,7 @@ public class CompletableFuture implements Future, CompletionStage {
* existing CompletionException.
*/
final boolean completeThrowable(Throwable x, Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x, r));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x, r));
}
/**
@@ -351,10 +361,11 @@ public class CompletableFuture implements Future, CompletionStage {
*/
static Object encodeRelay(Object r) {
Throwable x;
- return (((r instanceof AltResult) &&
- (x = ((AltResult)r).ex) != null &&
- !(x instanceof CompletionException)) ?
- new AltResult(new CompletionException(x)) : r);
+ if (r instanceof AltResult
+ && (x = ((AltResult)r).ex) != null
+ && !(x instanceof CompletionException))
+ r = new AltResult(new CompletionException(x));
+ return r;
}
/**
@@ -362,14 +373,13 @@ public class CompletableFuture implements Future, CompletionStage {
* If exceptional, r is first coerced to a CompletionException.
*/
final boolean completeRelay(Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeRelay(r));
+ return RESULT.compareAndSet(this, null, encodeRelay(r));
}
/**
* Reports result using Future.get conventions.
*/
- private static T reportGet(Object r)
+ private static Object reportGet(Object r)
throws InterruptedException, ExecutionException {
if (r == null) // by convention below, null means interrupted
throw new InterruptedException();
@@ -384,14 +394,13 @@ public class CompletableFuture implements Future, CompletionStage {
x = cause;
throw new ExecutionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/**
* Decodes outcome to return result or throw unchecked exception.
*/
- private static T reportJoin(Object r) {
+ private static Object reportJoin(Object r) {
if (r instanceof AltResult) {
Throwable x;
if ((x = ((AltResult)r).ex) == null)
@@ -402,8 +411,7 @@ public class CompletableFuture implements Future, CompletionStage {
throw (CompletionException)x;
throw new CompletionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/* ------------- Async task preliminaries -------------- */
@@ -449,12 +457,6 @@ public class CompletableFuture implements Future, CompletionStage {
static final int ASYNC = 1;
static final int NESTED = -1;
- /**
- * Spins before blocking in waitingGet
- */
- static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
- 1 << 8 : 0);
-
/* ------------- Base Completion classes and operations -------------- */
@SuppressWarnings("serial")
@@ -479,10 +481,6 @@ public class CompletableFuture implements Future, CompletionStage {
public final void setRawResult(Void v) {}
}
- static void lazySetNext(Completion c, Completion next) {
- U.putObjectRelease(c, NEXT, next);
- }
-
/**
* Pops and tries to trigger all reachable dependents. Call only
* when known to be done.
@@ -497,40 +495,47 @@ public class CompletableFuture implements Future, CompletionStage {
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture> d; Completion t;
- if (f.casStack(h, t = h.next)) {
+ if (STACK.compareAndSet(f, h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
- h.next = null; // detach
+ NEXT.compareAndSet(h, t, null); // try to detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
- /** Traverses stack and unlinks dead Completions. */
+ /** Traverses stack and unlinks one or more dead Completions, if found. */
final void cleanStack() {
- for (Completion p = null, q = stack; q != null;) {
+ Completion p = stack;
+ // ensure head of stack live
+ for (boolean unlinked = false;;) {
+ if (p == null)
+ return;
+ else if (p.isLive()) {
+ if (unlinked)
+ return;
+ else
+ break;
+ }
+ else if (STACK.weakCompareAndSetVolatile(this, p, (p = p.next)))
+ unlinked = true;
+ else
+ p = stack;
+ }
+ // try to unlink first non-live
+ for (Completion q = p.next; q != null;) {
Completion s = q.next;
if (q.isLive()) {
p = q;
q = s;
- }
- else if (p == null) {
- casStack(q, s);
- q = stack;
- }
- else {
- p.next = s;
- if (p.isLive())
- q = s;
- else {
- p = null; // restart
- q = stack;
- }
- }
+ } else if (NEXT.weakCompareAndSetVolatile(p, q, s))
+ break;
+ else
+ q = p.next;
}
}
@@ -568,24 +573,34 @@ public class CompletableFuture implements Future, CompletionStage {
final boolean isLive() { return dep != null; }
}
- /** Pushes the given completion (if it exists) unless done. */
- final void push(UniCompletion,?> c) {
+ /**
+ * Pushes the given completion unless it completes while trying.
+ * Caller should first check that result is null.
+ */
+ final void unipush(Completion c) {
if (c != null) {
- while (result == null && !tryPushStack(c))
- lazySetNext(c, null); // clear on failure
+ while (!tryPushStack(c)) {
+ if (result != null) {
+ NEXT.set(c, null);
+ break;
+ }
+ }
+ if (result != null)
+ c.tryFire(SYNC);
}
}
/**
- * Post-processing by dependent after successful UniCompletion
- * tryFire. Tries to clean stack of source a, and then either runs
- * postComplete or returns this to caller, depending on mode.
+ * Post-processing by dependent after successful UniCompletion tryFire.
+ * Tries to clean stack of source a, and then either runs postComplete
+ * or returns this to caller, depending on mode.
*/
final CompletableFuture postFire(CompletableFuture> a, int mode) {
if (a != null && a.stack != null) {
- if (a.result == null)
+ Object r;
+ if ((r = a.result) == null)
a.cleanStack();
- else if (mode >= 0)
+ if (mode >= 0 && (r != null || a.result != null))
a.postComplete();
}
if (result != null && stack != null) {
@@ -607,48 +622,65 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniApply(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Function super T,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ d.completeValue(f.apply(t));
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniApply(CompletableFuture a,
- Function super S,? extends T> f,
- UniApply c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- completeValue(f.apply(s));
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniApplyStage(
Executor e, Function super T,? extends V> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniApplyNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniApply(this, f, null)) {
- UniApply c = new UniApply(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniApply(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniApplyNow(
+ Object r, Executor e, Function super T,? extends V> f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.result = encodeThrowable(x, r);
+ return d;
+ }
+ r = null;
+ }
+ try {
+ if (e != null) {
+ e.execute(new UniApply(null, d, this, f));
+ } else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ d.result = d.encodeValue(f.apply(t));
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
}
return d;
}
@@ -662,48 +694,67 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniAccept(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Consumer super T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ f.accept(t);
+ d.completeNull();
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniAccept(CompletableFuture a,
- Consumer super S> f, UniAccept c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- f.accept(s);
- completeNull();
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniAcceptStage(Executor e,
Consumer super T> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniAcceptNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniAccept(this, f, null)) {
- UniAccept c = new UniAccept(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniAccept(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniAcceptNow(
+ Object r, Executor e, Consumer super T> f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.result = encodeThrowable(x, r);
+ return d;
+ }
+ r = null;
+ }
+ try {
+ if (e != null) {
+ e.execute(new UniAccept(null, d, this, f));
+ } else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ f.accept(t);
+ d.result = NIL;
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
}
return d;
}
@@ -717,42 +768,56 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniRun(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Runnable f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ if (d.result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ d.completeThrowable(x, r);
+ else
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ f.run();
+ d.completeNull();
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniRun(CompletableFuture> a, Runnable f, UniRun> c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- if (result == null) {
- if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
- completeThrowable(x, r);
- else
- try {
- if (c != null && !c.claim())
- return false;
- f.run();
- completeNull();
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniRunNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniRun(this, f, null)) {
- UniRun c = new UniRun(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ unipush(new UniRun(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniRunNow(Object r, Executor e, Runnable f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ d.result = encodeThrowable(x, r);
+ else
+ try {
+ if (e != null) {
+ e.execute(new UniRun(null, d, this, f));
+ } else {
+ f.run();
+ d.result = NIL;
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -766,20 +831,20 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this))
+ Object r; BiConsumer super T, ? super Throwable> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniWhenComplete(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniWhenComplete(CompletableFuture a,
+ final boolean uniWhenComplete(Object r,
BiConsumer super T,? super Throwable> f,
UniWhenComplete c) {
- Object r; T t; Throwable x = null;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ T t; Throwable x = null;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -811,10 +876,17 @@ public class CompletableFuture implements Future, CompletionStage {
Executor e, BiConsumer super T, ? super Throwable> f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniWhenComplete(this, f, null)) {
- UniWhenComplete c = new UniWhenComplete(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniWhenComplete(e, d, this, f));
+ else if (e == null)
+ d.uniWhenComplete(r, f, null);
+ else {
+ try {
+ e.execute(new UniWhenComplete(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -829,20 +901,20 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniHandle(a = src, fn, mode > 0 ? null : this))
+ Object r; BiFunction super T, Throwable, ? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniHandle(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniHandle(CompletableFuture a,
+ final boolean uniHandle(Object r,
BiFunction super S, Throwable, ? extends T> f,
UniHandle c) {
- Object r; S s; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ S s; Throwable x;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -867,10 +939,17 @@ public class CompletableFuture implements Future, CompletionStage {
Executor e, BiFunction super T, Throwable, ? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniHandle(this, f, null)) {
- UniHandle c = new UniHandle(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniHandle(e, d, this, f));
+ else if (e == null)
+ d.uniHandle(r, f, null);
+ else {
+ try {
+ e.execute(new UniHandle(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -885,19 +964,20 @@ public class CompletableFuture implements Future, CompletionStage {
final CompletableFuture tryFire(int mode) { // never ASYNC
// assert mode != ASYNC;
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this))
+ Object r; Function super Throwable, ? extends T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniExceptionally(r, f, this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniExceptionally(CompletableFuture a,
+ final boolean uniExceptionally(Object r,
Function super Throwable, ? extends T> f,
UniExceptionally c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ Throwable x;
if (result == null) {
try {
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) {
@@ -917,47 +997,39 @@ public class CompletableFuture implements Future, CompletionStage {
Function f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (!d.uniExceptionally(this, f, null)) {
- UniExceptionally c = new UniExceptionally(d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniExceptionally(d, this, f));
+ else
+ d.uniExceptionally(r, f, null);
return d;
}
@SuppressWarnings("serial")
- static final class UniRelay extends UniCompletion { // for Compose
- UniRelay(CompletableFuture dep, CompletableFuture src) {
+ static final class UniRelay extends UniCompletion {
+ UniRelay(CompletableFuture dep, CompletableFuture src) {
super(null, dep, src);
}
- final CompletableFuture tryFire(int mode) {
- CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null || !d.uniRelay(a = src))
+ final CompletableFuture tryFire(int mode) {
+ CompletableFuture d; CompletableFuture a; Object r;
+ if ((d = dep) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ if (d.result == null)
+ d.completeRelay(r);
src = null; dep = null;
return d.postFire(a, mode);
}
}
- final boolean uniRelay(CompletableFuture a) {
+ private static CompletableFuture uniCopyStage(
+ CompletableFuture src) {
Object r;
- if (a == null || (r = a.result) == null)
- return false;
- if (result == null) // no need to claim
- completeRelay(r);
- return true;
- }
-
- private CompletableFuture uniCopyStage() {
- Object r;
- CompletableFuture d = newIncompleteFuture();
- if ((r = result) != null)
- d.completeRelay(r);
- else {
- UniRelay c = new UniRelay(d, this);
- push(c);
- c.tryFire(SYNC);
- }
+ CompletableFuture d = src.newIncompleteFuture();
+ if ((r = src.result) != null)
+ d.result = encodeRelay(r);
+ else
+ src.unipush(new UniRelay(d, src));
return d;
}
@@ -966,9 +1038,7 @@ public class CompletableFuture implements Future, CompletionStage {
if ((r = result) != null)
return new MinimalStage(encodeRelay(r));
MinimalStage d = new MinimalStage();
- UniRelay c = new UniRelay(d, this);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniRelay(d, this));
return d;
}
@@ -982,54 +1052,48 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniCompose(a = src, fn, mode > 0 ? null : this))
+ Function super T, ? extends CompletionStage> f;
+ Object r; Throwable x;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ @SuppressWarnings("unchecked") T t = (T) r;
+ CompletableFuture g = f.apply(t).toCompletableFuture();
+ if ((r = g.result) != null)
+ d.completeRelay(r);
+ else {
+ g.unipush(new UniRelay(d, g));
+ if (d.result == null)
+ return null;
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniCompose(
- CompletableFuture a,
- Function super S, ? extends CompletionStage> f,
- UniCompose c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- CompletableFuture g = f.apply(s).toCompletableFuture();
- if (g.result == null || !uniRelay(g)) {
- UniRelay copy = new UniRelay(this, g);
- g.push(copy);
- copy.tryFire(SYNC);
- if (result == null)
- return false;
- }
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniComposeStage(
Executor e, Function super T, ? extends CompletionStage> f) {
if (f == null) throw new NullPointerException();
- Object r, s; Throwable x;
CompletableFuture d = newIncompleteFuture();
- if (e == null && (r = result) != null) {
+ Object r, s; Throwable x;
+ if ((r = result) == null)
+ unipush(new UniCompose(e, d, this, f));
+ else if (e == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
d.result = encodeThrowable(x, r);
@@ -1041,21 +1105,20 @@ public class CompletableFuture implements Future, CompletionStage {
@SuppressWarnings("unchecked") T t = (T) r;
CompletableFuture g = f.apply(t).toCompletableFuture();
if ((s = g.result) != null)
- d.completeRelay(s);
+ d.result = encodeRelay(s);
else {
- UniRelay c = new UniRelay(d, g);
- g.push(c);
- c.tryFire(SYNC);
+ g.unipush(new UniRelay(d, g));
}
- return d;
} catch (Throwable ex) {
d.result = encodeThrowable(ex);
- return d;
}
}
- UniCompose c = new UniCompose(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ else
+ try {
+ e.execute(new UniCompose(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -1085,21 +1148,28 @@ public class CompletableFuture implements Future, CompletionStage {
}
final boolean isLive() {
BiCompletion,?,?> c;
- return (c = base) != null && c.dep != null;
+ return (c = base) != null
+ // && c.isLive()
+ && c.dep != null;
}
}
- /** Pushes completion to this and b unless both done. */
+ /**
+ * Pushes completion to this and b unless both done.
+ * Caller should first check that either result or b.result is null.
+ */
final void bipush(CompletableFuture> b, BiCompletion,?,?> c) {
if (c != null) {
- Object r;
- while ((r = result) == null && !tryPushStack(c))
- lazySetNext(c, null); // clear on failure
- if (b != null && b != this && b.result == null) {
- Completion q = (r != null) ? c : new CoCompletion(c);
- while (b.result == null && !b.tryPushStack(q))
- lazySetNext(q, null); // clear on failure
+ while (result == null) {
+ if (tryPushStack(c)) {
+ if (b.result == null)
+ b.unipush(new CoCompletion(c));
+ else if (result != null)
+ c.tryFire(SYNC);
+ return;
+ }
}
+ b.unipush(c);
}
}
@@ -1107,9 +1177,10 @@ public class CompletableFuture implements Future, CompletionStage {
final CompletableFuture postFire(CompletableFuture> a,
CompletableFuture> b, int mode) {
if (b != null && b.stack != null) { // clean second source
- if (b.result == null)
+ Object r;
+ if ((r = b.result) == null)
b.cleanStack();
- else if (mode >= 0)
+ if (mode >= 0 && (r != null || b.result != null))
b.postComplete();
}
return postFire(a, mode);
@@ -1127,22 +1198,21 @@ public class CompletableFuture implements Future, CompletionStage {
CompletableFuture d;
CompletableFuture a;
CompletableFuture b;
- if ((d = dep) == null ||
- !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r, s; BiFunction super T,? super U,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || (b = snd) == null || (s = b.result) == null
+ || !d.biApply(r, s, f, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final boolean biApply(CompletableFuture a,
- CompletableFuture b,
+ final boolean biApply(Object r, Object s,
BiFunction super R,? super S,? extends T> f,
BiApply c) {
- Object r, s; Throwable x;
- if (a == null || (r = a.result) == null ||
- b == null || (s = b.result) == null || f == null)
- return false;
+ Throwable x;
tryComplete: if (result == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
@@ -1174,15 +1244,20 @@ public class CompletableFuture implements Future, CompletionStage {
private CompletableFuture