--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java Thu Sep 29 17:23:47 2016 +0000
@@ -68,6 +68,7 @@
Type type = db.lookupType("InstanceKlass");
arrayKlasses = new MetadataField(type.getAddressField("_array_klasses"), 0);
methods = type.getAddressField("_methods");
+ defaultMethods = type.getAddressField("_default_methods");
methodOrdering = type.getAddressField("_method_ordering");
localInterfaces = type.getAddressField("_local_interfaces");
transitiveInterfaces = type.getAddressField("_transitive_interfaces");
@@ -128,6 +129,7 @@
private static MetadataField arrayKlasses;
private static AddressField methods;
+ private static AddressField defaultMethods;
private static AddressField methodOrdering;
private static AddressField localInterfaces;
private static AddressField transitiveInterfaces;
@@ -335,6 +337,20 @@
// Accessors for declared fields
public Klass getArrayKlasses() { return (Klass) arrayKlasses.getValue(this); }
public MethodArray getMethods() { return new MethodArray(methods.getValue(getAddress())); }
+
+ public MethodArray getDefaultMethods() {
+ if (defaultMethods != null) {
+ Address addr = defaultMethods.getValue(getAddress());
+ if ((addr != null) && (addr.getAddressAt(0) != null)) {
+ return new MethodArray(addr);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
public KlassArray getLocalInterfaces() { return new KlassArray(localInterfaces.getValue(getAddress())); }
public KlassArray getTransitiveInterfaces() { return new KlassArray(transitiveInterfaces.getValue(getAddress())); }
public int getJavaFieldsCount() { return (int) javaFieldsCount.getValue(this); }
--- a/hotspot/src/share/vm/classfile/classLoader.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/classLoader.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -1358,7 +1358,7 @@
if (!Universe::is_module_initialized() &&
!ModuleEntryTable::javabase_defined() &&
mod_entry == NULL) {
- mod_entry = ModuleEntryTable::javabase_module();
+ mod_entry = ModuleEntryTable::javabase_moduleEntry();
}
// The module must be a named module
@@ -1708,7 +1708,7 @@
if (jb_module == NULL) {
vm_exit_during_initialization("Unable to create ModuleEntry for java.base");
}
- ModuleEntryTable::set_javabase_module(jb_module);
+ ModuleEntryTable::set_javabase_moduleEntry(jb_module);
}
}
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -773,6 +773,41 @@
InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, mirror, CHECK);
}
+// Set the java.lang.reflect.Module module field in the java_lang_Class mirror
+void java_lang_Class::set_mirror_module_field(KlassHandle k, Handle mirror, Handle module, TRAPS) {
+ if (module.is_null()) {
+ // During startup, the module may be NULL only if java.base has not been defined yet.
+ // Put the class on the fixup_module_list to patch later when the java.lang.reflect.Module
+ // for java.base is known.
+ assert(!Universe::is_module_initialized(), "Incorrect java.lang.reflect.Module pre module system initialization");
+ MutexLocker m1(Module_lock, THREAD);
+ // Keep list of classes needing java.base module fixup
+ if (!ModuleEntryTable::javabase_defined()) {
+ if (fixup_module_field_list() == NULL) {
+ GrowableArray<Klass*>* list =
+ new (ResourceObj::C_HEAP, mtModule) GrowableArray<Klass*>(500, true);
+ set_fixup_module_field_list(list);
+ }
+ k->class_loader_data()->inc_keep_alive();
+ fixup_module_field_list()->push(k());
+ } else {
+ // java.base was defined at some point between calling create_mirror()
+ // and obtaining the Module_lock, patch this particular class with java.base.
+ ModuleEntry *javabase_entry = ModuleEntryTable::javabase_moduleEntry();
+ assert(javabase_entry != NULL && javabase_entry->module() != NULL,
+ "Setting class module field, java.base should be defined");
+ Handle javabase_handle(THREAD, JNIHandles::resolve(javabase_entry->module()));
+ set_module(mirror(), javabase_handle());
+ }
+ } else {
+ assert(Universe::is_module_initialized() ||
+ (ModuleEntryTable::javabase_defined() &&
+ (module() == JNIHandles::resolve(ModuleEntryTable::javabase_moduleEntry()->module()))),
+ "Incorrect java.lang.reflect.Module specification while creating mirror");
+ set_module(mirror(), module());
+ }
+}
+
void java_lang_Class::create_mirror(KlassHandle k, Handle class_loader,
Handle module, Handle protection_domain, TRAPS) {
assert(k->java_mirror() == NULL, "should only assign mirror once");
@@ -835,25 +870,13 @@
set_class_loader(mirror(), class_loader());
// set the module field in the java_lang_Class instance
- // This may be null during bootstrap but will get fixed up later on.
- set_module(mirror(), module());
+ set_mirror_module_field(k, mirror, module, THREAD);
// Setup indirection from klass->mirror last
// after any exceptions can happen during allocations.
if (!k.is_null()) {
k->set_java_mirror(mirror());
}
-
- // Keep list of classes needing java.base module fixup.
- if (!ModuleEntryTable::javabase_defined()) {
- if (fixup_module_field_list() == NULL) {
- GrowableArray<Klass*>* list =
- new (ResourceObj::C_HEAP, mtModule) GrowableArray<Klass*>(500, true);
- set_fixup_module_field_list(list);
- }
- k->class_loader_data()->inc_keep_alive();
- fixup_module_field_list()->push(k());
- }
} else {
if (fixup_mirror_list() == NULL) {
GrowableArray<Klass*>* list =
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -219,6 +219,7 @@
static void set_class_loader(oop java_class, oop class_loader);
static void set_component_mirror(oop java_class, oop comp_mirror);
static void initialize_mirror_fields(KlassHandle k, Handle mirror, Handle protection_domain, TRAPS);
+ static void set_mirror_module_field(KlassHandle K, Handle mirror, Handle module, TRAPS);
public:
static void compute_offsets();
--- a/hotspot/src/share/vm/classfile/klassFactory.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/klassFactory.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -25,12 +25,85 @@
#include "precompiled.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
+#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp"
+#include "classfile/classLoaderData.inline.hpp"
#include "classfile/klassFactory.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "prims/jvmtiEnvBase.hpp"
+#include "prims/jvmtiRedefineClasses.hpp"
#include "trace/traceMacros.hpp"
+// called during initial loading of a shared class
+instanceKlassHandle KlassFactory::check_shared_class_file_load_hook(
+ instanceKlassHandle ik,
+ Symbol* class_name,
+ Handle class_loader,
+ Handle protection_domain, TRAPS) {
+#if INCLUDE_CDS && INCLUDE_JVMTI
+ assert(ik.not_null(), "sanity");
+ assert(ik()->is_shared(), "expecting a shared class");
+
+ if (JvmtiExport::should_post_class_file_load_hook()) {
+ assert(THREAD->is_Java_thread(), "must be JavaThread");
+
+ // Post the CFLH
+ JvmtiCachedClassFileData* cached_class_file = NULL;
+ JvmtiCachedClassFileData* archived_class_data = ik->get_archived_class_data();
+ assert(archived_class_data != NULL, "shared class has no archived class data");
+ unsigned char* ptr =
+ VM_RedefineClasses::get_cached_class_file_bytes(archived_class_data);
+ unsigned char* end_ptr =
+ ptr + VM_RedefineClasses::get_cached_class_file_len(archived_class_data);
+ unsigned char* old_ptr = ptr;
+ JvmtiExport::post_class_file_load_hook(class_name,
+ class_loader,
+ protection_domain,
+ &ptr,
+ &end_ptr,
+ &cached_class_file);
+ if (old_ptr != ptr) {
+ // JVMTI agent has modified class file data.
+ // Set new class file stream using JVMTI agent modified class file data.
+ ClassLoaderData* loader_data =
+ ClassLoaderData::class_loader_data(class_loader());
+ int path_index = ik->shared_classpath_index();
+ SharedClassPathEntry* ent =
+ (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index);
+ ClassFileStream* stream = new ClassFileStream(ptr,
+ end_ptr - ptr,
+ ent->_name,
+ ClassFileStream::verify);
+ ClassFileParser parser(stream,
+ class_name,
+ loader_data,
+ protection_domain,
+ NULL,
+ NULL,
+ ClassFileParser::BROADCAST, // publicity level
+ CHECK_NULL);
+ instanceKlassHandle new_ik = parser.create_instance_klass(true /* changed_by_loadhook */,
+ CHECK_NULL);
+ if (cached_class_file != NULL) {
+ new_ik->set_cached_class_file(cached_class_file);
+ }
+
+ if (class_loader.is_null()) {
+ ResourceMark rm;
+ ClassLoader::add_package(class_name->as_C_string(), path_index, THREAD);
+ }
+
+ return new_ik;
+ }
+ }
+#endif
+
+ return NULL;
+}
+
+
static ClassFileStream* check_class_file_load_hook(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
@@ -97,7 +170,6 @@
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS) {
-
assert(stream != NULL, "invariant");
assert(loader_data != NULL, "invariant");
assert(THREAD->is_Java_thread(), "must be a JavaThread");
@@ -142,5 +214,27 @@
TRACE_KLASS_CREATION(result, parser, THREAD);
+#if INCLUDE_CDS && INCLUDE_JVMTI
+ if (DumpSharedSpaces) {
+ assert(cached_class_file == NULL, "Sanity");
+ // Archive the class stream data into the optional data section
+ JvmtiCachedClassFileData *p;
+ int len;
+ const unsigned char *bytes;
+ // event based tracing might set cached_class_file
+ if ((bytes = result->get_cached_class_file_bytes()) != NULL) {
+ len = result->get_cached_class_file_len();
+ } else {
+ len = stream->length();
+ bytes = stream->buffer();
+ }
+ p = (JvmtiCachedClassFileData*)MetaspaceShared::optional_data_space_alloc(
+ offset_of(JvmtiCachedClassFileData, data) + len);
+ p->length = len;
+ memcpy(p->data, bytes, len);
+ result->set_archived_class_data(p);
+ }
+#endif
+
return result;
}
--- a/hotspot/src/share/vm/classfile/klassFactory.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/klassFactory.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -75,6 +75,12 @@
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS);
+ public:
+ static instanceKlassHandle check_shared_class_file_load_hook(
+ instanceKlassHandle ik,
+ Symbol* class_name,
+ Handle class_loader,
+ Handle protection_domain, TRAPS);
};
#endif // SHARE_VM_CLASSFILE_KLASSFACTORY_HPP
--- a/hotspot/src/share/vm/classfile/moduleEntry.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -92,7 +92,7 @@
// read java.base. If either of these conditions
// hold, readability has been established.
if (!this->is_named() ||
- (m == ModuleEntryTable::javabase_module())) {
+ (m == ModuleEntryTable::javabase_moduleEntry())) {
return true;
}
@@ -358,16 +358,27 @@
}
// Set java.lang.reflect.Module, version and location for java.base
- ModuleEntry* jb_module = javabase_module();
+ ModuleEntry* jb_module = javabase_moduleEntry();
assert(jb_module != NULL, "java.base ModuleEntry not defined");
- jb_module->set_module(boot_loader_data->add_handle(module_handle));
jb_module->set_version(version);
jb_module->set_location(location);
+ // Once java.base's ModuleEntry _module field is set with the known
+ // java.lang.reflect.Module, java.base is considered "defined" to the VM.
+ jb_module->set_module(boot_loader_data->add_handle(module_handle));
+
// Store pointer to the ModuleEntry for java.base in the java.lang.reflect.Module object.
java_lang_reflect_Module::set_module_entry(module_handle(), jb_module);
+
+ // Patch any previously loaded classes' module field with java.base's java.lang.reflect.Module.
+ patch_javabase_entries(module_handle);
}
+// Within java.lang.Class instances there is a java.lang.reflect.Module field
+// that must be set with the defining module. During startup, prior to java.base's
+// definition, classes needing their module field set are added to the fixup_module_list.
+// Their module field is set once java.base's java.lang.reflect.Module is known to the VM.
void ModuleEntryTable::patch_javabase_entries(Handle module_handle) {
+ assert(Module_lock->owned_by_self(), "should have the Module_lock");
if (module_handle.is_null()) {
fatal("Unable to patch the module field of classes loaded prior to java.base's definition, invalid java.lang.reflect.Module");
}
@@ -389,9 +400,7 @@
for (int i = 0; i < list_length; i++) {
Klass* k = list->at(i);
assert(k->is_klass(), "List should only hold classes");
- Thread* THREAD = Thread::current();
- KlassHandle kh(THREAD, k);
- java_lang_Class::fixup_module_field(kh, module_handle);
+ java_lang_Class::fixup_module_field(KlassHandle(k), module_handle);
k->class_loader_data()->dec_keep_alive();
}
--- a/hotspot/src/share/vm/classfile/moduleEntry.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -78,11 +78,11 @@
_must_walk_reads = false;
}
- Symbol* name() const { return literal(); }
- void set_name(Symbol* n) { set_literal(n); }
+ Symbol* name() const { return literal(); }
+ void set_name(Symbol* n) { set_literal(n); }
- jobject module() const { return _module; }
- void set_module(jobject j) { _module = j; }
+ jobject module() const { return _module; }
+ void set_module(jobject j) { _module = j; }
// The shared ProtectionDomain reference is set once the VM loads a shared class
// originated from the current Module. The referenced ProtectionDomain object is
@@ -217,13 +217,13 @@
// Special handling for unnamed module, one per class loader's ModuleEntryTable
void create_unnamed_module(ClassLoaderData* loader_data);
- ModuleEntry* unnamed_module() { return _unnamed_module; }
+ ModuleEntry* unnamed_module() { return _unnamed_module; }
// Special handling for java.base
- static ModuleEntry* javabase_module() { return _javabase_module; }
- static void set_javabase_module(ModuleEntry* java_base) { _javabase_module = java_base; }
- static bool javabase_defined() { return ((_javabase_module != NULL) &&
- (_javabase_module->module() != NULL)); }
+ static ModuleEntry* javabase_moduleEntry() { return _javabase_module; }
+ static void set_javabase_moduleEntry(ModuleEntry* java_base) { _javabase_module = java_base; }
+ static bool javabase_defined() { return ((_javabase_module != NULL) &&
+ (_javabase_module->module() != NULL)); }
static void finalize_javabase(Handle module_handle, Symbol* version, Symbol* location);
static void patch_javabase_entries(Handle module_handle);
--- a/hotspot/src/share/vm/classfile/modules.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/modules.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -206,7 +206,7 @@
assert(pkg_list->length() == 0 || package_table != NULL, "Bad package_table");
// Ensure java.base's ModuleEntry has been created
- assert(ModuleEntryTable::javabase_module() != NULL, "No ModuleEntry for java.base");
+ assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "No ModuleEntry for java.base");
bool duplicate_javabase = false;
{
@@ -226,7 +226,7 @@
for (int x = 0; x < pkg_list->length(); x++) {
// Some of java.base's packages were added early in bootstrapping, ignore duplicates.
if (package_table->lookup_only(pkg_list->at(x)) == NULL) {
- pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_module());
+ pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_moduleEntry());
assert(pkg != NULL, "Unable to create a java.base package entry");
}
// Unable to have a GrowableArray of TempNewSymbol. Must decrement the refcount of
@@ -255,9 +255,6 @@
log_trace(modules)("define_javabase_module(): creation of package %s for module java.base",
(pkg_list->at(x))->as_C_string());
}
-
- // Patch any previously loaded classes' module field with java.base's jlr.Module.
- ModuleEntryTable::patch_javabase_entries(module_handle);
}
void Modules::define_module(jobject module, jstring version,
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -1210,16 +1210,12 @@
instanceKlassHandle SystemDictionary::load_shared_class(
Symbol* class_name, Handle class_loader, TRAPS) {
- // Don't load shared class when JvmtiExport::should_post_class_file_load_hook()
- // is enabled since posting CFLH is not supported when loading shared class.
- if (!JvmtiExport::should_post_class_file_load_hook()) {
- instanceKlassHandle ik (THREAD, find_shared_class(class_name));
- // Make sure we only return the boot class for the NULL classloader.
- if (ik.not_null() &&
- ik->is_shared_boot_class() && class_loader.is_null()) {
- Handle protection_domain;
- return load_shared_class(ik, class_loader, protection_domain, THREAD);
- }
+ instanceKlassHandle ik (THREAD, find_shared_class(class_name));
+ // Make sure we only return the boot class for the NULL classloader.
+ if (ik.not_null() &&
+ ik->is_shared_boot_class() && class_loader.is_null()) {
+ Handle protection_domain;
+ return load_shared_class(ik, class_loader, protection_domain, THREAD);
}
return instanceKlassHandle();
}
@@ -1303,11 +1299,6 @@
Handle class_loader,
Handle protection_domain, TRAPS) {
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
- if (JvmtiExport::should_post_class_file_load_hook()) {
- // Don't load shared class when JvmtiExport::should_post_class_file_load_hook()
- // is enabled since posting CFLH is not supported when loading shared class.
- return nh;
- }
if (ik.not_null()) {
Symbol* class_name = ik->name();
@@ -1358,6 +1349,14 @@
}
}
+ instanceKlassHandle new_ik = KlassFactory::check_shared_class_file_load_hook(
+ ik, class_name, class_loader, protection_domain, CHECK_(nh));
+ if (new_ik.not_null()) {
+ // The class is changed by CFLH. Return the new class. The shared class is
+ // not used.
+ return new_ik;
+ }
+
// Adjust methods to recover missing data. They need addresses for
// interpreter entry points and their default native method address
// must be reset.
--- a/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -1366,22 +1366,25 @@
return false;
}
assert(prefix != NULL && prefix != BUSY, "Error");
- size_t i = 1;
oop cur = prefix;
- while (i < objsFromOverflow && cur->klass_or_null() != NULL) {
- i++; cur = cur->list_ptr_from_klass();
+ for (size_t i = 1; i < objsFromOverflow; ++i) {
+ oop next = cur->list_ptr_from_klass();
+ if (next == NULL) break;
+ cur = next;
}
+ assert(cur != NULL, "Loop postcondition");
// Reattach remaining (suffix) to overflow list
- if (cur->klass_or_null() == NULL) {
+ oop suffix = cur->list_ptr_from_klass();
+ if (suffix == NULL) {
// Write back the NULL in lieu of the BUSY we wrote
// above and it is still the same value.
if (_overflow_list == BUSY) {
(void) Atomic::cmpxchg_ptr(NULL, &_overflow_list, BUSY);
}
} else {
- assert(cur->klass_or_null() != (Klass*)(address)BUSY, "Error");
- oop suffix = cur->list_ptr_from_klass(); // suffix will be put back on global list
+ assert(suffix != BUSY, "Error");
+ // suffix will be put back on global list
cur->set_klass_to_list_ptr(NULL); // break off suffix
// It's possible that the list is still in the empty(busy) state
// we left it in a short while ago; in that case we may be
@@ -1401,8 +1404,10 @@
// Too bad, someone else got in in between; we'll need to do a splice.
// Find the last item of suffix list
oop last = suffix;
- while (last->klass_or_null() != NULL) {
- last = last->list_ptr_from_klass();
+ while (true) {
+ oop next = last->list_ptr_from_klass();
+ if (next == NULL) break;
+ last = next;
}
// Atomically prepend suffix to current overflow list
observed_overflow_list = _overflow_list;
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -1479,7 +1479,7 @@
"Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B min_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)",
capacity_after_gc, used_after_gc, minimum_desired_capacity, MinHeapFreeRatio);
- expand(expand_bytes);
+ expand(expand_bytes, _workers);
// No expansion, now see if we want to shrink
} else if (capacity_after_gc > maximum_desired_capacity) {
@@ -1599,7 +1599,7 @@
word_size * HeapWordSize);
- if (expand(expand_bytes)) {
+ if (expand(expand_bytes, _workers)) {
_hrm.verify_optional();
_verifier->verify_region_sets_optional();
return attempt_allocation_at_safepoint(word_size,
@@ -1609,7 +1609,7 @@
return NULL;
}
-bool G1CollectedHeap::expand(size_t expand_bytes, double* expand_time_ms) {
+bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, double* expand_time_ms) {
size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes);
aligned_expand_bytes = align_size_up(aligned_expand_bytes,
HeapRegion::GrainBytes);
@@ -1626,7 +1626,7 @@
uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes);
assert(regions_to_expand > 0, "Must expand by at least one region");
- uint expanded_by = _hrm.expand_by(regions_to_expand);
+ uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers);
if (expand_time_ms != NULL) {
*expand_time_ms = (os::elapsedTime() - expand_heap_start_time_sec) * MILLIUNITS;
}
@@ -1927,7 +1927,7 @@
_cmThread = _cm->cmThread();
// Now expand into the initial heap size.
- if (!expand(init_byte_size)) {
+ if (!expand(init_byte_size, _workers)) {
vm_shutdown_during_initialization("Failed to allocate initial heap.");
return JNI_ENOMEM;
}
@@ -3165,7 +3165,6 @@
assert(_verifier->check_cset_fast_test(), "Inconsistency in the InCSetState table.");
- _cm->note_start_of_gc();
// We call this after finalize_cset() to
// ensure that the CSet has been finalized.
_cm->verify_no_cset_oops();
@@ -3241,7 +3240,7 @@
// No need for an ergo logging here,
// expansion_amount() does this when it returns a value > 0.
double expand_ms;
- if (!expand(expand_bytes, &expand_ms)) {
+ if (!expand(expand_bytes, _workers, &expand_ms)) {
// We failed to expand the heap. Cannot do anything about it.
}
g1_policy()->phase_times()->record_expand_heap_time(expand_ms);
@@ -3251,7 +3250,6 @@
// We redo the verification but now wrt to the new CSet which
// has just got initialized after the previous CSet was freed.
_cm->verify_no_cset_oops();
- _cm->note_end_of_gc();
// This timing is only used by the ergonomics to handle our pause target.
// It is unclear why this should not include the full pause. We will
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -557,7 +557,7 @@
// Returns true if the heap was expanded by the requested amount;
// false otherwise.
// (Rounds up to a HeapRegion boundary.)
- bool expand(size_t expand_bytes, double* expand_time_ms = NULL);
+ bool expand(size_t expand_bytes, WorkGang* pretouch_workers = NULL, double* expand_time_ms = NULL);
// Returns the PLAB statistics for a given destination.
inline G1EvacStats* alloc_buffer_stats(InCSetState dest);
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -133,129 +133,184 @@
}
G1CMMarkStack::G1CMMarkStack() :
- _reserved_space(),
+ _max_chunk_capacity(0),
_base(NULL),
- _capacity(0),
- _saved_index((size_t)AllBits),
+ _chunk_capacity(0),
+ _out_of_memory(false),
_should_expand(false) {
set_empty();
}
bool G1CMMarkStack::resize(size_t new_capacity) {
assert(is_empty(), "Only resize when stack is empty.");
- assert(new_capacity <= MarkStackSizeMax,
- "Trying to resize stack to " SIZE_FORMAT " elements when the maximum is " SIZE_FORMAT, new_capacity, MarkStackSizeMax);
-
- size_t reservation_size = ReservedSpace::allocation_align_size_up(new_capacity * sizeof(oop));
-
- ReservedSpace rs(reservation_size);
- if (!rs.is_reserved()) {
- log_warning(gc)("Failed to reserve memory for new overflow mark stack with " SIZE_FORMAT " elements and size " SIZE_FORMAT "B.", new_capacity, reservation_size);
+ assert(new_capacity <= _max_chunk_capacity,
+ "Trying to resize stack to " SIZE_FORMAT " chunks when the maximum is " SIZE_FORMAT, new_capacity, _max_chunk_capacity);
+
+ OopChunk* new_base = MmapArrayAllocator<OopChunk, mtGC>::allocate_or_null(new_capacity);
+
+ if (new_base == NULL) {
+ log_warning(gc)("Failed to reserve memory for new overflow mark stack with " SIZE_FORMAT " chunks and size " SIZE_FORMAT "B.", new_capacity, new_capacity * sizeof(OopChunk));
return false;
}
-
- VirtualSpace vs;
-
- if (!vs.initialize(rs, rs.size())) {
- rs.release();
- log_warning(gc)("Failed to commit memory for new overflow mark stack of size " SIZE_FORMAT "B.", rs.size());
- return false;
+ // Release old mapping.
+ if (_base != NULL) {
+ MmapArrayAllocator<OopChunk, mtGC>::free(_base, _chunk_capacity);
}
- assert(vs.committed_size() == rs.size(), "Failed to commit all of the mark stack.");
-
- // Release old mapping.
- _reserved_space.release();
-
- // Save new mapping for future unmapping.
- _reserved_space = rs;
-
- MemTracker::record_virtual_memory_type((address)_reserved_space.base(), mtGC);
-
- _base = (oop*) vs.low();
- _capacity = new_capacity;
+ _base = new_base;
+ _chunk_capacity = new_capacity;
set_empty();
_should_expand = false;
return true;
}
-bool G1CMMarkStack::allocate(size_t capacity) {
- return resize(capacity);
+size_t G1CMMarkStack::capacity_alignment() {
+ return (size_t)lcm(os::vm_allocation_granularity(), sizeof(OopChunk)) / sizeof(void*);
+}
+
+bool G1CMMarkStack::initialize(size_t initial_capacity, size_t max_capacity) {
+ guarantee(_max_chunk_capacity == 0, "G1CMMarkStack already initialized.");
+
+ size_t const OopChunkSizeInVoidStar = sizeof(OopChunk) / sizeof(void*);
+
+ _max_chunk_capacity = (size_t)align_size_up(max_capacity, capacity_alignment()) / OopChunkSizeInVoidStar;
+ size_t initial_chunk_capacity = (size_t)align_size_up(initial_capacity, capacity_alignment()) / OopChunkSizeInVoidStar;
+
+ guarantee(initial_chunk_capacity <= _max_chunk_capacity,
+ "Maximum chunk capacity " SIZE_FORMAT " smaller than initial capacity " SIZE_FORMAT,
+ _max_chunk_capacity,
+ initial_chunk_capacity);
+
+ log_debug(gc)("Initialize mark stack with " SIZE_FORMAT " chunks, maximum " SIZE_FORMAT,
+ initial_chunk_capacity, _max_chunk_capacity);
+
+ return resize(initial_chunk_capacity);
}
void G1CMMarkStack::expand() {
// Clear expansion flag
_should_expand = false;
- if (_capacity == MarkStackSizeMax) {
- log_debug(gc)("Can not expand overflow mark stack further, already at maximum capacity of " SIZE_FORMAT " elements.", _capacity);
+ if (_chunk_capacity == _max_chunk_capacity) {
+ log_debug(gc)("Can not expand overflow mark stack further, already at maximum capacity of " SIZE_FORMAT " chunks.", _chunk_capacity);
return;
}
- size_t old_capacity = _capacity;
+ size_t old_capacity = _chunk_capacity;
// Double capacity if possible
- size_t new_capacity = MIN2(old_capacity * 2, MarkStackSizeMax);
+ size_t new_capacity = MIN2(old_capacity * 2, _max_chunk_capacity);
if (resize(new_capacity)) {
- log_debug(gc)("Expanded marking stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " elements",
+ log_debug(gc)("Expanded mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks",
old_capacity, new_capacity);
} else {
- log_warning(gc)("Failed to expand marking stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " elements",
+ log_warning(gc)("Failed to expand mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks",
old_capacity, new_capacity);
}
}
G1CMMarkStack::~G1CMMarkStack() {
if (_base != NULL) {
- _base = NULL;
- _reserved_space.release();
- }
-}
-
-void G1CMMarkStack::par_push_arr(oop* buffer, size_t n) {
- MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
- size_t start = _index;
- size_t next_index = start + n;
- if (next_index > _capacity) {
- _overflow = true;
- return;
- }
- // Otherwise.
- _index = next_index;
- for (size_t i = 0; i < n; i++) {
- size_t ind = start + i;
- assert(ind < _capacity, "By overflow test above.");
- _base[ind] = buffer[i];
+ MmapArrayAllocator<OopChunk, mtGC>::free(_base, _chunk_capacity);
}
}
-bool G1CMMarkStack::par_pop_arr(oop* buffer, size_t max, size_t* n) {
- MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
- size_t index = _index;
- if (index == 0) {
- *n = 0;
+void G1CMMarkStack::add_chunk_to_list(OopChunk* volatile* list, OopChunk* elem) {
+ elem->next = *list;
+ *list = elem;
+}
+
+void G1CMMarkStack::add_chunk_to_chunk_list(OopChunk* elem) {
+ MutexLockerEx x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag);
+ add_chunk_to_list(&_chunk_list, elem);
+ _chunks_in_chunk_list++;
+}
+
+void G1CMMarkStack::add_chunk_to_free_list(OopChunk* elem) {
+ MutexLockerEx x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag);
+ add_chunk_to_list(&_free_list, elem);
+}
+
+G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_list(OopChunk* volatile* list) {
+ OopChunk* result = *list;
+ if (result != NULL) {
+ *list = (*list)->next;
+ }
+ return result;
+}
+
+G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_chunk_list() {
+ MutexLockerEx x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag);
+ OopChunk* result = remove_chunk_from_list(&_chunk_list);
+ if (result != NULL) {
+ _chunks_in_chunk_list--;
+ }
+ return result;
+}
+
+G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_free_list() {
+ MutexLockerEx x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag);
+ return remove_chunk_from_list(&_free_list);
+}
+
+G1CMMarkStack::OopChunk* G1CMMarkStack::allocate_new_chunk() {
+ // This dirty read of _hwm is okay because we only ever increase the _hwm in parallel code.
+ // Further this limits _hwm to a value of _chunk_capacity + #threads, avoiding
+ // wraparound of _hwm.
+ if (_hwm >= _chunk_capacity) {
+ return NULL;
+ }
+
+ size_t cur_idx = Atomic::add(1, &_hwm) - 1;
+ if (cur_idx >= _chunk_capacity) {
+ return NULL;
+ }
+
+ OopChunk* result = ::new (&_base[cur_idx]) OopChunk;
+ result->next = NULL;
+ return result;
+}
+
+bool G1CMMarkStack::par_push_chunk(oop* ptr_arr) {
+ // Get a new chunk.
+ OopChunk* new_chunk = remove_chunk_from_free_list();
+
+ if (new_chunk == NULL) {
+ // Did not get a chunk from the free list. Allocate from backing memory.
+ new_chunk = allocate_new_chunk();
+ }
+
+ if (new_chunk == NULL) {
+ _out_of_memory = true;
return false;
- } else {
- size_t k = MIN2(max, index);
- size_t new_ind = index - k;
- for (size_t j = 0; j < k; j++) {
- buffer[j] = _base[new_ind + j];
- }
- _index = new_ind;
- *n = k;
- return true;
}
+
+ Copy::conjoint_memory_atomic(ptr_arr, new_chunk->data, OopsPerChunk * sizeof(oop));
+
+ add_chunk_to_chunk_list(new_chunk);
+
+ return true;
}
-void G1CMMarkStack::note_start_of_gc() {
- assert(_saved_index == (size_t)AllBits, "note_start_of_gc()/end_of_gc() calls bracketed incorrectly");
- _saved_index = _index;
+bool G1CMMarkStack::par_pop_chunk(oop* ptr_arr) {
+ OopChunk* cur = remove_chunk_from_chunk_list();
+
+ if (cur == NULL) {
+ return false;
+ }
+
+ Copy::conjoint_memory_atomic(cur->data, ptr_arr, OopsPerChunk * sizeof(oop));
+
+ add_chunk_to_free_list(cur);
+ return true;
}
-void G1CMMarkStack::note_end_of_gc() {
- guarantee(!stack_modified(), "Saved index " SIZE_FORMAT " must be the same as " SIZE_FORMAT, _saved_index, _index);
-
- _saved_index = (size_t)AllBits;
+void G1CMMarkStack::set_empty() {
+ _chunks_in_chunk_list = 0;
+ _hwm = 0;
+ clear_out_of_memory();
+ _chunk_list = NULL;
+ _free_list = NULL;
}
G1CMRootRegions::G1CMRootRegions() :
@@ -483,9 +538,8 @@
}
}
- if (!_global_mark_stack.allocate(MarkStackSize)) {
+ if (!_global_mark_stack.initialize(MarkStackSize, MarkStackSizeMax)) {
vm_exit_during_initialization("Failed to allocate initial concurrent mark overflow mark stack.");
- return;
}
_tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_worker_id, mtGC);
@@ -1695,10 +1749,10 @@
// oop closures will set the has_overflown flag if we overflow the
// global marking stack.
- assert(_global_mark_stack.overflow() || _global_mark_stack.is_empty(),
- "mark stack should be empty (unless it overflowed)");
-
- if (_global_mark_stack.overflow()) {
+ assert(_global_mark_stack.is_out_of_memory() || _global_mark_stack.is_empty(),
+ "Mark stack should be empty (unless it is out of memory)");
+
+ if (_global_mark_stack.is_out_of_memory()) {
// This should have been done already when we tried to push an
// entry on to the global mark stack. But let's do it again.
set_has_overflown();
@@ -2343,49 +2397,54 @@
}
void G1CMTask::move_entries_to_global_stack() {
- // local array where we'll store the entries that will be popped
- // from the local queue
- oop buffer[global_stack_transfer_size];
-
- int n = 0;
+ // Local array where we'll store the entries that will be popped
+ // from the local queue.
+ oop buffer[G1CMMarkStack::OopsPerChunk];
+
+ size_t n = 0;
oop obj;
- while (n < global_stack_transfer_size && _task_queue->pop_local(obj)) {
+ while (n < G1CMMarkStack::OopsPerChunk && _task_queue->pop_local(obj)) {
buffer[n] = obj;
++n;
}
+ if (n < G1CMMarkStack::OopsPerChunk) {
+ buffer[n] = NULL;
+ }
if (n > 0) {
- // we popped at least one entry from the local queue
-
- if (!_cm->mark_stack_push(buffer, n)) {
+ if (!_cm->mark_stack_push(buffer)) {
set_has_aborted();
}
}
- // this operation was quite expensive, so decrease the limits
+ // This operation was quite expensive, so decrease the limits.
decrease_limits();
}
-void G1CMTask::get_entries_from_global_stack() {
- // local array where we'll store the entries that will be popped
+bool G1CMTask::get_entries_from_global_stack() {
+ // Local array where we'll store the entries that will be popped
// from the global stack.
- oop buffer[global_stack_transfer_size];
- size_t n;
- _cm->mark_stack_pop(buffer, global_stack_transfer_size, &n);
- assert(n <= global_stack_transfer_size,
- "we should not pop more than the given limit");
- if (n > 0) {
- // yes, we did actually pop at least one entry
- for (size_t i = 0; i < n; ++i) {
- bool success = _task_queue->push(buffer[i]);
- // We only call this when the local queue is empty or under a
- // given target limit. So, we do not expect this push to fail.
- assert(success, "invariant");
+ oop buffer[G1CMMarkStack::OopsPerChunk];
+
+ if (!_cm->mark_stack_pop(buffer)) {
+ return false;
+ }
+
+ // We did actually pop at least one entry.
+ for (size_t i = 0; i < G1CMMarkStack::OopsPerChunk; ++i) {
+ oop elem = buffer[i];
+ if (elem == NULL) {
+ break;
}
+ bool success = _task_queue->push(elem);
+ // We only call this when the local queue is empty or under a
+ // given target limit. So, we do not expect this push to fail.
+ assert(success, "invariant");
}
- // this operation was quite expensive, so decrease the limits
+ // This operation was quite expensive, so decrease the limits
decrease_limits();
+ return true;
}
void G1CMTask::drain_local_queue(bool partially) {
@@ -2429,20 +2488,21 @@
// Decide what the target size is, depending whether we're going to
// drain it partially (so that other tasks can steal if they run out
- // of things to do) or totally (at the very end). Notice that,
- // because we move entries from the global stack in chunks or
- // because another task might be doing the same, we might in fact
- // drop below the target. But, this is not a problem.
- size_t target_size;
+ // of things to do) or totally (at the very end).
+ // Notice that when draining the global mark stack partially, due to the racyness
+ // of the mark stack size update we might in fact drop below the target. But,
+ // this is not a problem.
+ // In case of total draining, we simply process until the global mark stack is
+ // totally empty, disregarding the size counter.
if (partially) {
- target_size = _cm->partial_mark_stack_size_target();
+ size_t const target_size = _cm->partial_mark_stack_size_target();
+ while (!has_aborted() && _cm->mark_stack_size() > target_size) {
+ if (get_entries_from_global_stack()) {
+ drain_local_queue(partially);
+ }
+ }
} else {
- target_size = 0;
- }
-
- if (_cm->mark_stack_size() > target_size) {
- while (!has_aborted() && _cm->mark_stack_size() > target_size) {
- get_entries_from_global_stack();
+ while (!has_aborted() && get_entries_from_global_stack()) {
drain_local_queue(partially);
}
}
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -149,42 +149,98 @@
//
// Stores oops in a huge buffer in virtual memory that is always fully committed.
// Resizing may only happen during a STW pause when the stack is empty.
+//
+// Memory is allocated on a "chunk" basis, i.e. a set of oops. For this, the mark
+// stack memory is split into evenly sized chunks of oops. Users can only
+// add or remove entries on that basis.
+// Chunks are filled in increasing address order. Not completely filled chunks
+// have a NULL element as a terminating element.
+//
+// Every chunk has a header containing a single pointer element used for memory
+// management. This wastes some space, but is negligible (< .1% with current sizing).
+//
+// Memory management is done using a mix of tracking a high water-mark indicating
+// that all chunks at a lower address are valid chunks, and a singly linked free
+// list connecting all empty chunks.
class G1CMMarkStack VALUE_OBJ_CLASS_SPEC {
- ReservedSpace _reserved_space; // Space currently reserved for the mark stack.
+public:
+ // Number of oops that can fit in a single chunk.
+ static const size_t OopsPerChunk = 1024 - 1 /* One reference for the next pointer */;
+private:
+ struct OopChunk {
+ OopChunk* next;
+ oop data[OopsPerChunk];
+ };
+
+ size_t _max_chunk_capacity; // Maximum number of OopChunk elements on the stack.
+
+ OopChunk* _base; // Bottom address of allocated memory area.
+ size_t _chunk_capacity; // Current maximum number of OopChunk elements.
- oop* _base; // Bottom address of allocated memory area.
- size_t _capacity; // Maximum number of elements.
- size_t _index; // One more than last occupied index.
+ char _pad0[DEFAULT_CACHE_LINE_SIZE];
+ OopChunk* volatile _free_list; // Linked list of free chunks that can be allocated by users.
+ char _pad1[DEFAULT_CACHE_LINE_SIZE - sizeof(OopChunk*)];
+ OopChunk* volatile _chunk_list; // List of chunks currently containing data.
+ volatile size_t _chunks_in_chunk_list;
+ char _pad2[DEFAULT_CACHE_LINE_SIZE - sizeof(OopChunk*) - sizeof(size_t)];
+
+ volatile size_t _hwm; // High water mark within the reserved space.
+ char _pad4[DEFAULT_CACHE_LINE_SIZE - sizeof(size_t)];
+
+ // Allocate a new chunk from the reserved memory, using the high water mark. Returns
+ // NULL if out of memory.
+ OopChunk* allocate_new_chunk();
- size_t _saved_index; // Value of _index saved at start of GC to detect mark stack modifications during that time.
+ volatile bool _out_of_memory;
- bool _overflow;
+ // Atomically add the given chunk to the list.
+ void add_chunk_to_list(OopChunk* volatile* list, OopChunk* elem);
+ // Atomically remove and return a chunk from the given list. Returns NULL if the
+ // list is empty.
+ OopChunk* remove_chunk_from_list(OopChunk* volatile* list);
+
+ void add_chunk_to_chunk_list(OopChunk* elem);
+ void add_chunk_to_free_list(OopChunk* elem);
+
+ OopChunk* remove_chunk_from_chunk_list();
+ OopChunk* remove_chunk_from_free_list();
+
bool _should_expand;
// Resizes the mark stack to the given new capacity. Releases any previous
// memory if successful.
bool resize(size_t new_capacity);
- bool stack_modified() const { return _index != _saved_index; }
public:
G1CMMarkStack();
~G1CMMarkStack();
- bool allocate(size_t capacity);
+ // Alignment and minimum capacity of this mark stack in number of oops.
+ static size_t capacity_alignment();
+
+ // Allocate and initialize the mark stack with the given number of oops.
+ bool initialize(size_t initial_capacity, size_t max_capacity);
- // Pushes the first "n" elements of the given buffer on the stack.
- void par_push_arr(oop* buffer, size_t n);
+ // Pushes the given buffer containing at most OopsPerChunk elements on the mark
+ // stack. If less than OopsPerChunk elements are to be pushed, the array must
+ // be terminated with a NULL.
+ // Returns whether the buffer contents were successfully pushed to the global mark
+ // stack.
+ bool par_push_chunk(oop* buffer);
- // Moves up to max elements from the stack into the given buffer. Returns
- // the number of elements pushed, and false if the array has been empty.
- // Returns true if the buffer contains at least one element.
- bool par_pop_arr(oop* buffer, size_t max, size_t* n);
+ // Pops a chunk from this mark stack, copying them into the given buffer. This
+ // chunk may contain up to OopsPerChunk elements. If there are less, the last
+ // element in the array is a NULL pointer.
+ bool par_pop_chunk(oop* buffer);
- bool is_empty() const { return _index == 0; }
- size_t capacity() const { return _capacity; }
+ // Return whether the chunk list is empty. Racy due to unsynchronized access to
+ // _chunk_list.
+ bool is_empty() const { return _chunk_list == NULL; }
- bool overflow() const { return _overflow; }
- void clear_overflow() { _overflow = false; }
+ size_t capacity() const { return _chunk_capacity; }
+
+ bool is_out_of_memory() const { return _out_of_memory; }
+ void clear_out_of_memory() { _out_of_memory = false; }
bool should_expand() const { return _should_expand; }
void set_should_expand(bool value) { _should_expand = value; }
@@ -192,20 +248,15 @@
// Expand the stack, typically in response to an overflow condition
void expand();
- size_t size() const { return _index; }
-
- void set_empty() { _index = 0; clear_overflow(); }
-
- // Record the current index.
- void note_start_of_gc();
+ // Return the approximate number of oops on this mark stack. Racy due to
+ // unsynchronized access to _chunks_in_chunk_list.
+ size_t size() const { return _chunks_in_chunk_list * OopsPerChunk; }
- // Make sure that we have not added any entries to the stack during GC.
- void note_end_of_gc();
+ void set_empty();
- // Apply fn to each oop in the mark stack, up to the bound recorded
- // via one of the above "note" functions. The mark stack must not
+ // Apply Fn to every oop on the mark stack. The mark stack must not
// be modified while iterating.
- template<typename Fn> void iterate(Fn fn);
+ template<typename Fn> void iterate(Fn fn) const PRODUCT_RETURN;
};
// Root Regions are regions that are not empty at the beginning of a
@@ -278,7 +329,6 @@
friend class G1CMDrainMarkingStackClosure;
friend class G1CMBitMapClosure;
friend class G1CMConcurrentMarkingTask;
- friend class G1CMMarkStack;
friend class G1CMRemarkTask;
friend class G1CMTask;
@@ -479,22 +529,20 @@
public:
// Manipulation of the global mark stack.
// The push and pop operations are used by tasks for transfers
- // between task-local queues and the global mark stack, and use
- // locking for concurrency safety.
- bool mark_stack_push(oop* arr, size_t n) {
- _global_mark_stack.par_push_arr(arr, n);
- if (_global_mark_stack.overflow()) {
+ // between task-local queues and the global mark stack.
+ bool mark_stack_push(oop* arr) {
+ if (!_global_mark_stack.par_push_chunk(arr)) {
set_has_overflown();
return false;
}
return true;
}
- void mark_stack_pop(oop* arr, size_t max, size_t* n) {
- _global_mark_stack.par_pop_arr(arr, max, n);
+ bool mark_stack_pop(oop* arr) {
+ return _global_mark_stack.par_pop_chunk(arr);
}
size_t mark_stack_size() { return _global_mark_stack.size(); }
size_t partial_mark_stack_size_target() { return _global_mark_stack.capacity()/3; }
- bool mark_stack_overflow() { return _global_mark_stack.overflow(); }
+ bool mark_stack_overflow() { return _global_mark_stack.is_out_of_memory(); }
bool mark_stack_empty() { return _global_mark_stack.is_empty(); }
G1CMRootRegions* root_regions() { return &_root_regions; }
@@ -599,16 +647,6 @@
// read-only, so use this carefully!
void clearRangePrevBitmap(MemRegion mr);
- // Notify data structures that a GC has started.
- void note_start_of_gc() {
- _global_mark_stack.note_start_of_gc();
- }
-
- // Notify data structures that a GC is finished.
- void note_end_of_gc() {
- _global_mark_stack.note_end_of_gc();
- }
-
// Verify that there are no CSet oops on the stacks (taskqueues /
// global mark stack) and fingers (global / per-task).
// If marking is not in progress, it's a no-op.
@@ -670,10 +708,7 @@
// references reaches this limit
refs_reached_period = 384,
// Initial value for the hash seed, used in the work stealing code
- init_hash_seed = 17,
- // How many entries will be transferred between global stack and
- // local queues at once.
- global_stack_transfer_size = 1024
+ init_hash_seed = 17
};
uint _worker_id;
@@ -858,9 +893,10 @@
// It pushes an object on the local queue.
inline void push(oop obj);
- // These two move entries to/from the global stack.
+ // Move entries to the global stack.
void move_entries_to_global_stack();
- void get_entries_from_global_stack();
+ // Move entries from the global stack, return true if we were successful to do so.
+ bool get_entries_from_global_stack();
// It pops and scans objects from the local queue. If partially is
// true, then it stops when the queue size is of a given limit. If
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.inline.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.inline.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -89,14 +89,28 @@
#undef check_mark
+#ifndef PRODUCT
template<typename Fn>
-inline void G1CMMarkStack::iterate(Fn fn) {
+inline void G1CMMarkStack::iterate(Fn fn) const {
assert_at_safepoint(true);
- assert(!stack_modified(), "Saved index " SIZE_FORMAT " must be the same as " SIZE_FORMAT, _saved_index, _index);
- for (size_t i = 0; i < _index; ++i) {
- fn(_base[i]);
+
+ size_t num_chunks = 0;
+
+ OopChunk* cur = _chunk_list;
+ while (cur != NULL) {
+ guarantee(num_chunks <= _chunks_in_chunk_list, "Found " SIZE_FORMAT " oop chunks which is more than there should be", num_chunks);
+
+ for (size_t i = 0; i < OopsPerChunk; ++i) {
+ if (cur->data[i] == NULL) {
+ break;
+ }
+ fn(cur->data[i]);
+ }
+ cur = cur->next;
+ num_chunks++;
}
}
+#endif
// It scans an object and visits its children.
inline void G1CMTask::scan_object(oop obj) { process_grey_object<true>(obj); }
--- a/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -34,7 +34,6 @@
class G1ConcurrentMark;
class DirtyCardToOopClosure;
class G1CMBitMap;
-class G1CMMarkStack;
class G1ParScanThreadState;
class G1CMTask;
class ReferenceProcessor;
--- a/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -24,8 +24,10 @@
#include "precompiled.hpp"
#include "gc/g1/g1PageBasedVirtualSpace.hpp"
+#include "gc/shared/workgroup.hpp"
#include "oops/markOop.hpp"
#include "oops/oop.inline.hpp"
+#include "runtime/atomic.hpp"
#include "runtime/os.inline.hpp"
#include "services/memTracker.hpp"
#include "utilities/bitMap.inline.hpp"
@@ -177,7 +179,7 @@
guarantee(start_page < end_page,
"Given start page " SIZE_FORMAT " is larger or equal to end page " SIZE_FORMAT, start_page, end_page);
- os::pretouch_memory(page_start(start_page), bounded_end_addr(end_page));
+ os::pretouch_memory(page_start(start_page), bounded_end_addr(end_page), _page_size);
}
bool G1PageBasedVirtualSpace::commit(size_t start_page, size_t size_in_pages) {
@@ -198,9 +200,6 @@
}
_committed.set_range(start_page, end_page);
- if (AlwaysPreTouch) {
- pretouch_internal(start_page, end_page);
- }
return zero_filled;
}
@@ -227,6 +226,53 @@
_committed.clear_range(start_page, end_page);
}
+class G1PretouchTask : public AbstractGangTask {
+private:
+ char* volatile _cur_addr;
+ char* const _start_addr;
+ char* const _end_addr;
+ size_t const _page_size;
+public:
+ G1PretouchTask(char* start_address, char* end_address, size_t page_size) :
+ AbstractGangTask("G1 PreTouch",
+ Universe::is_fully_initialized() ? GCId::current_raw() :
+ // During VM initialization there is
+ // no GC cycle that this task can be
+ // associated with.
+ GCId::undefined()),
+ _cur_addr(start_address),
+ _start_addr(start_address),
+ _end_addr(end_address),
+ _page_size(page_size) {
+ }
+
+ virtual void work(uint worker_id) {
+ size_t const actual_chunk_size = MAX2(chunk_size(), _page_size);
+ while (true) {
+ char* touch_addr = (char*)Atomic::add_ptr((intptr_t)actual_chunk_size, (volatile void*) &_cur_addr) - actual_chunk_size;
+ if (touch_addr < _start_addr || touch_addr >= _end_addr) {
+ break;
+ }
+ char* end_addr = touch_addr + MIN2(actual_chunk_size, pointer_delta(_end_addr, touch_addr, sizeof(char)));
+ os::pretouch_memory(touch_addr, end_addr, _page_size);
+ }
+ }
+
+ static size_t chunk_size() { return PreTouchParallelChunkSize; }
+};
+
+void G1PageBasedVirtualSpace::pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang) {
+ guarantee(pretouch_gang != NULL, "No pretouch gang specified.");
+
+ size_t num_chunks = MAX2((size_t)1, size_in_pages * _page_size / MAX2(G1PretouchTask::chunk_size(), _page_size));
+
+ uint num_workers = MIN2((uint)num_chunks, pretouch_gang->active_workers());
+ G1PretouchTask cl(page_start(start_page), bounded_end_addr(start_page + size_in_pages), _page_size);
+ log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.",
+ cl.name(), num_workers, num_chunks, size_in_pages * _page_size);
+ pretouch_gang->run_task(&cl, num_workers);
+}
+
bool G1PageBasedVirtualSpace::contains(const void* p) const {
return _low_boundary <= (const char*) p && (const char*) p < _high_boundary;
}
--- a/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -30,6 +30,8 @@
#include "memory/virtualspace.hpp"
#include "utilities/bitMap.hpp"
+class WorkGang;
+
// Virtual space management helper for a virtual space with an OS page allocation
// granularity.
// (De-)Allocation requests are always OS page aligned by passing a page index
@@ -117,6 +119,8 @@
// Uncommit the given area of pages starting at start being size_in_pages large.
void uncommit(size_t start_page, size_t size_in_pages);
+ void pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang = NULL);
+
// Initialize the given reserved space with the given base address and the size
// actually used.
// Prefer to commit in page_size chunks.
--- a/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -66,8 +66,12 @@
guarantee(alloc_granularity >= page_size, "allocation granularity smaller than commit granularity");
}
- virtual void commit_regions(uint start_idx, size_t num_regions) {
- bool zero_filled = _storage.commit((size_t)start_idx * _pages_per_region, num_regions * _pages_per_region);
+ virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) {
+ size_t const start_page = (size_t)start_idx * _pages_per_region;
+ bool zero_filled = _storage.commit(start_page, num_regions * _pages_per_region);
+ if (AlwaysPreTouch) {
+ _storage.pretouch(start_page, num_regions * _pages_per_region, pretouch_gang);
+ }
_commit_map.set_range(start_idx, start_idx + num_regions);
fire_on_commit(start_idx, num_regions, zero_filled);
}
@@ -110,19 +114,38 @@
_refcounts.initialize((HeapWord*)rs.base(), (HeapWord*)(rs.base() + align_size_up(rs.size(), page_size)), page_size);
}
- virtual void commit_regions(uint start_idx, size_t num_regions) {
+ virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) {
+ size_t const NoPage = ~(size_t)0;
+
+ size_t first_committed = NoPage;
+ size_t num_committed = 0;
+
+ bool all_zero_filled = true;
+
for (uint i = start_idx; i < start_idx + num_regions; i++) {
assert(!_commit_map.at(i), "Trying to commit storage at region %u that is already committed", i);
size_t idx = region_idx_to_page_idx(i);
uint old_refcount = _refcounts.get_by_index(idx);
+
bool zero_filled = false;
if (old_refcount == 0) {
+ if (first_committed == NoPage) {
+ first_committed = idx;
+ num_committed = 1;
+ } else {
+ num_committed++;
+ }
zero_filled = _storage.commit(idx, 1);
}
+ all_zero_filled &= zero_filled;
+
_refcounts.set_by_index(idx, old_refcount + 1);
_commit_map.set_bit(i);
- fire_on_commit(i, 1, zero_filled);
}
+ if (AlwaysPreTouch && num_committed > 0) {
+ _storage.pretouch(first_committed, num_committed, pretouch_gang);
+ }
+ fire_on_commit(start_idx, num_regions, all_zero_filled);
}
virtual void uncommit_regions(uint start_idx, size_t num_regions) {
--- a/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -29,6 +29,8 @@
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
+class WorkGang;
+
class G1MappingChangedListener VALUE_OBJ_CLASS_SPEC {
public:
// Fired after commit of the memory, i.e. the memory this listener is registered
@@ -68,7 +70,7 @@
return _commit_map.at(idx);
}
- virtual void commit_regions(uint start_idx, size_t num_regions = 1) = 0;
+ virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL) = 0;
virtual void uncommit_regions(uint start_idx, size_t num_regions = 1) = 0;
// Creates an appropriate G1RegionToSpaceMapper for the given parameters.
--- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -353,35 +353,6 @@
}
HeapWord*
-HeapRegion::object_iterate_mem_careful(MemRegion mr,
- ObjectClosure* cl) {
- G1CollectedHeap* g1h = G1CollectedHeap::heap();
- // We used to use "block_start_careful" here. But we're actually happy
- // to update the BOT while we do this...
- HeapWord* cur = block_start(mr.start());
- mr = mr.intersection(used_region());
- if (mr.is_empty()) return NULL;
- // Otherwise, find the obj that extends onto mr.start().
-
- assert(cur <= mr.start()
- && (oop(cur)->klass_or_null() == NULL ||
- cur + oop(cur)->size() > mr.start()),
- "postcondition of block_start");
- oop obj;
- while (cur < mr.end()) {
- obj = oop(cur);
- if (obj->klass_or_null() == NULL) {
- // Ran into an unparseable point.
- return cur;
- } else if (!g1h->is_obj_dead(obj)) {
- cl->do_object(obj);
- }
- cur += block_size(cur);
- }
- return NULL;
-}
-
-HeapWord*
HeapRegion::
oops_on_card_seq_iterate_careful(MemRegion mr,
FilterOutOfRegionClosure* cl,
--- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -653,17 +653,6 @@
}
}
- // Requires that "mr" be entirely within the region.
- // Apply "cl->do_object" to all objects that intersect with "mr".
- // If the iteration encounters an unparseable portion of the region,
- // or if "cl->abort()" is true after a closure application,
- // terminate the iteration and return the address of the start of the
- // subregion that isn't done. (The two can be distinguished by querying
- // "cl->abort()".) Return of "NULL" indicates that the iteration
- // completed.
- HeapWord*
- object_iterate_mem_careful(MemRegion mr, ObjectClosure* cl);
-
// filter_young: if true and the region is a young region then we
// skip the iteration.
// card_ptr: if not NULL, and we decide that the card is not young
--- a/hotspot/src/share/vm/gc/g1/heapRegionManager.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegionManager.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -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
@@ -72,22 +72,22 @@
return g1h->new_heap_region(hrm_index, mr);
}
-void HeapRegionManager::commit_regions(uint index, size_t num_regions) {
+void HeapRegionManager::commit_regions(uint index, size_t num_regions, WorkGang* pretouch_gang) {
guarantee(num_regions > 0, "Must commit more than zero regions");
guarantee(_num_committed + num_regions <= max_length(), "Cannot commit more than the maximum amount of regions");
_num_committed += (uint)num_regions;
- _heap_mapper->commit_regions(index, num_regions);
+ _heap_mapper->commit_regions(index, num_regions, pretouch_gang);
// Also commit auxiliary data
- _prev_bitmap_mapper->commit_regions(index, num_regions);
- _next_bitmap_mapper->commit_regions(index, num_regions);
+ _prev_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang);
+ _next_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang);
- _bot_mapper->commit_regions(index, num_regions);
- _cardtable_mapper->commit_regions(index, num_regions);
+ _bot_mapper->commit_regions(index, num_regions, pretouch_gang);
+ _cardtable_mapper->commit_regions(index, num_regions, pretouch_gang);
- _card_counts_mapper->commit_regions(index, num_regions);
+ _card_counts_mapper->commit_regions(index, num_regions, pretouch_gang);
}
void HeapRegionManager::uncommit_regions(uint start, size_t num_regions) {
@@ -117,9 +117,9 @@
_card_counts_mapper->uncommit_regions(start, num_regions);
}
-void HeapRegionManager::make_regions_available(uint start, uint num_regions) {
+void HeapRegionManager::make_regions_available(uint start, uint num_regions, WorkGang* pretouch_gang) {
guarantee(num_regions > 0, "No point in calling this for zero regions");
- commit_regions(start, num_regions);
+ commit_regions(start, num_regions, pretouch_gang);
for (uint i = start; i < start + num_regions; i++) {
if (_regions.get_by_index(i) == NULL) {
HeapRegion* new_hr = new_heap_region(i);
@@ -163,11 +163,11 @@
return MemoryUsage(0, used_sz, committed_sz, committed_sz);
}
-uint HeapRegionManager::expand_by(uint num_regions) {
- return expand_at(0, num_regions);
+uint HeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) {
+ return expand_at(0, num_regions, pretouch_workers);
}
-uint HeapRegionManager::expand_at(uint start, uint num_regions) {
+uint HeapRegionManager::expand_at(uint start, uint num_regions, WorkGang* pretouch_workers) {
if (num_regions == 0) {
return 0;
}
@@ -181,7 +181,7 @@
while (expanded < num_regions &&
(num_last_found = find_unavailable_from_idx(cur, &idx_last_found)) > 0) {
uint to_expand = MIN2(num_regions - expanded, num_last_found);
- make_regions_available(idx_last_found, to_expand);
+ make_regions_available(idx_last_found, to_expand, pretouch_workers);
expanded += to_expand;
cur = idx_last_found + num_last_found + 1;
}
--- a/hotspot/src/share/vm/gc/g1/heapRegionManager.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegionManager.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -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
@@ -34,6 +34,7 @@
class HeapRegionClosure;
class HeapRegionClaimer;
class FreeRegionList;
+class WorkGang;
class G1HeapRegionTable : public G1BiasedMappedArray<HeapRegion*> {
protected:
@@ -94,10 +95,10 @@
HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); }
HeapWord* heap_end() const {return _regions.end_address_mapped(); }
- void make_regions_available(uint index, uint num_regions = 1);
+ void make_regions_available(uint index, uint num_regions = 1, WorkGang* pretouch_gang = NULL);
// Pass down commit calls to the VirtualSpace.
- void commit_regions(uint index, size_t num_regions = 1);
+ void commit_regions(uint index, size_t num_regions = 1, WorkGang* pretouch_gang = NULL);
void uncommit_regions(uint index, size_t num_regions = 1);
// Notify other data structures about change in the heap layout.
@@ -209,12 +210,12 @@
// HeapRegions, or re-use existing ones. Returns the number of regions the
// sequence was expanded by. If a HeapRegion allocation fails, the resulting
// number of regions might be smaller than what's desired.
- uint expand_by(uint num_regions);
+ uint expand_by(uint num_regions, WorkGang* pretouch_workers = NULL);
// Makes sure that the regions from start to start+num_regions-1 are available
// for allocation. Returns the number of regions that were committed to achieve
// this.
- uint expand_at(uint start, uint num_regions);
+ uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers = NULL);
// Find a contiguous set of empty regions of length num. Returns the start index of
// that set, or G1_NO_HRM_INDEX.
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -304,9 +304,6 @@
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);
-
// Raw memory allocation facilities
// The obj and array allocate methods are covers for these methods.
// mem_allocate() should never be
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -41,14 +41,22 @@
// Inline allocation implementations.
void CollectedHeap::post_allocation_setup_common(KlassHandle klass,
- HeapWord* obj) {
- post_allocation_setup_no_klass_install(klass, obj);
- post_allocation_install_obj_klass(klass, oop(obj));
+ HeapWord* obj_ptr) {
+ post_allocation_setup_no_klass_install(klass, obj_ptr);
+ oop obj = (oop)obj_ptr;
+#if ! INCLUDE_ALL_GCS
+ obj->set_klass(klass());
+#else
+ // Need a release store to ensure array/class length, mark word, and
+ // object zeroing are visible before setting the klass non-NULL, for
+ // concurrent collectors.
+ obj->release_set_klass(klass());
+#endif
}
void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass,
- HeapWord* objPtr) {
- oop obj = (oop)objPtr;
+ HeapWord* obj_ptr) {
+ oop obj = (oop)obj_ptr;
assert(obj != NULL, "NULL object pointer");
if (UseBiasedLocking && (klass() != NULL)) {
@@ -59,18 +67,6 @@
}
}
-void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass,
- oop obj) {
- // These asserts are kind of complicated because of klassKlass
- // and the beginning of the world.
- assert(klass() != NULL || !Universe::is_fully_initialized(), "NULL klass");
- assert(klass() == NULL || klass()->is_klass(), "not a klass");
- assert(obj != NULL, "NULL object pointer");
- obj->set_klass(klass());
- assert(!Universe::is_fully_initialized() || obj->klass() != NULL,
- "missing klass");
-}
-
// Support for jvmti and dtrace
inline void post_allocation_notify(KlassHandle klass, oop obj, int size) {
// support low memory notifications (no-op if not enabled)
@@ -88,25 +84,26 @@
}
void CollectedHeap::post_allocation_setup_obj(KlassHandle klass,
- HeapWord* obj,
+ HeapWord* obj_ptr,
int size) {
- post_allocation_setup_common(klass, obj);
+ post_allocation_setup_common(klass, obj_ptr);
+ oop obj = (oop)obj_ptr;
assert(Universe::is_bootstrapping() ||
- !((oop)obj)->is_array(), "must not be an array");
+ !obj->is_array(), "must not be an array");
// notify jvmti and dtrace
- post_allocation_notify(klass, (oop)obj, size);
+ post_allocation_notify(klass, obj, size);
}
void CollectedHeap::post_allocation_setup_class(KlassHandle klass,
- HeapWord* obj,
+ HeapWord* obj_ptr,
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;
+ // Set oop_size field before setting the _klass field because a
+ // non-NULL _klass field indicates that the object is parsable by
+ // concurrent GC.
+ oop new_cls = (oop)obj_ptr;
assert(size > 0, "oop_size must be positive.");
java_lang_Class::set_oop_size(new_cls, size);
- post_allocation_setup_common(klass, obj);
+ post_allocation_setup_common(klass, obj_ptr);
assert(Universe::is_bootstrapping() ||
!new_cls->is_array(), "must not be an array");
// notify jvmti and dtrace
@@ -114,15 +111,15 @@
}
void CollectedHeap::post_allocation_setup_array(KlassHandle klass,
- HeapWord* obj,
+ HeapWord* obj_ptr,
int length) {
- // Set array length before setting the _klass field
- // in post_allocation_setup_common() because the klass field
- // indicates that the object is parsable by concurrent GC.
+ // Set array length before setting the _klass field because a
+ // non-NULL klass field indicates that the object is parsable by
+ // concurrent GC.
assert(length >= 0, "length should be non-negative");
- ((arrayOop)obj)->set_length(length);
- post_allocation_setup_common(klass, obj);
- oop new_obj = (oop)obj;
+ ((arrayOop)obj_ptr)->set_length(length);
+ post_allocation_setup_common(klass, obj_ptr);
+ oop new_obj = (oop)obj_ptr;
assert(new_obj->is_array(), "must be an array");
// notify jvmti and dtrace (must be after length is set for dtrace)
post_allocation_notify(klass, new_obj, new_obj->size());
--- a/hotspot/src/share/vm/gc/shared/workgroup.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/gc/shared/workgroup.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -62,7 +62,12 @@
AbstractGangTask(const char* name) :
_name(name),
_gc_id(GCId::current_raw())
- {}
+ {}
+
+ AbstractGangTask(const char* name, const uint gc_id) :
+ _name(name),
+ _gc_id(gc_id)
+ {}
// The abstract work method.
// The argument tells you which member of the gang you are.
--- a/hotspot/src/share/vm/memory/allocation.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/allocation.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -738,6 +738,7 @@
static size_t size_for(size_t length);
public:
+ static E* allocate_or_null(size_t length);
static E* allocate(size_t length);
static void free(E* addr, size_t length);
};
--- a/hotspot/src/share/vm/memory/allocation.inline.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/allocation.inline.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -153,6 +153,24 @@
}
template <class E, MEMFLAGS F>
+E* MmapArrayAllocator<E, F>::allocate_or_null(size_t length) {
+ size_t size = size_for(length);
+ int alignment = os::vm_allocation_granularity();
+
+ char* addr = os::reserve_memory(size, NULL, alignment, F);
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ if (os::commit_memory(addr, size, !ExecMem, "Allocator (commit)")) {
+ return (E*)addr;
+ } else {
+ os::release_memory(addr, size);
+ return NULL;
+ }
+}
+
+template <class E, MEMFLAGS F>
E* MmapArrayAllocator<E, F>::allocate(size_t length) {
size_t size = size_for(length);
int alignment = os::vm_allocation_granularity();
--- a/hotspot/src/share/vm/memory/filemap.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/filemap.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -649,7 +649,7 @@
// Memory map a region in the address space.
static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode",
- "String1", "String2" };
+ "String1", "String2", "OptionalData" };
char* FileMapInfo::map_region(int i) {
assert(!MetaspaceShared::is_string_region(i), "sanity");
--- a/hotspot/src/share/vm/memory/filemap.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/filemap.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -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
@@ -252,10 +252,27 @@
bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false);
void print_shared_spaces() NOT_CDS_RETURN;
+ // The ro+rw+md+mc spaces size
+ static size_t core_spaces_size() {
+ return align_size_up((SharedReadOnlySize + SharedReadWriteSize +
+ SharedMiscDataSize + SharedMiscCodeSize),
+ os::vm_allocation_granularity());
+ }
+
+ // The estimated optional space size.
+ //
+ // Currently the optional space only has archived class bytes.
+ // The core_spaces_size is the size of all class metadata, which is a good
+ // estimate of the total class bytes to be archived. Only the portion
+ // containing data is written out to the archive and mapped at runtime.
+ // There is no memory waste due to unused portion in optional space.
+ static size_t optional_space_size() {
+ return core_spaces_size();
+ }
+
+ // Total shared_spaces size includes the ro, rw, md, mc and od spaces
static size_t shared_spaces_size() {
- return align_size_up(SharedReadOnlySize + SharedReadWriteSize +
- SharedMiscDataSize + SharedMiscCodeSize,
- os::vm_allocation_granularity());
+ return core_spaces_size() + optional_space_size();
}
// Stop CDS sharing and unmap CDS regions.
--- a/hotspot/src/share/vm/memory/metaspace.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/metaspace.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -3172,36 +3172,28 @@
address cds_address = NULL;
FileMapInfo* mapinfo = new FileMapInfo();
- if (JvmtiExport::should_post_class_file_load_hook()) {
- // Currently CDS does not support JVMTI CFLH when loading shared class.
- // If JvmtiExport::should_post_class_file_load_hook is already enabled,
- // just disable UseSharedSpaces.
- FileMapInfo::fail_continue("Tool agent requires sharing to be disabled.");
- delete mapinfo;
- } else {
- // Open the shared archive file, read and validate the header. If
- // initialization fails, shared spaces [UseSharedSpaces] are
- // disabled and the file is closed.
- // Map in spaces now also
- if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
- cds_total = FileMapInfo::shared_spaces_size();
- cds_address = (address)mapinfo->header()->region_addr(0);
+ // Open the shared archive file, read and validate the header. If
+ // initialization fails, shared spaces [UseSharedSpaces] are
+ // disabled and the file is closed.
+ // Map in spaces now also
+ if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
+ cds_total = FileMapInfo::shared_spaces_size();
+ cds_address = (address)mapinfo->header()->region_addr(0);
#ifdef _LP64
- if (using_class_space()) {
- char* cds_end = (char*)(cds_address + cds_total);
- cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment);
- // If UseCompressedClassPointers is set then allocate the metaspace area
- // above the heap and above the CDS area (if it exists).
- allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
- // Map the shared string space after compressed pointers
- // because it relies on compressed class pointers setting to work
- mapinfo->map_string_regions();
- }
+ if (using_class_space()) {
+ char* cds_end = (char*)(cds_address + cds_total);
+ cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment);
+ // If UseCompressedClassPointers is set then allocate the metaspace area
+ // above the heap and above the CDS area (if it exists).
+ allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
+ // Map the shared string space after compressed pointers
+ // because it relies on compressed class pointers setting to work
+ mapinfo->map_string_regions();
+ }
#endif // _LP64
- } else {
- assert(!mapinfo->is_open() && !UseSharedSpaces,
- "archive file not closed or shared spaces not disabled.");
- }
+ } else {
+ assert(!mapinfo->is_open() && !UseSharedSpaces,
+ "archive file not closed or shared spaces not disabled.");
}
}
#endif // INCLUDE_CDS
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -65,6 +65,7 @@
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
SharedMiscRegion MetaspaceShared::_mc;
SharedMiscRegion MetaspaceShared::_md;
+SharedMiscRegion MetaspaceShared::_od;
void SharedMiscRegion::initialize(ReservedSpace rs, size_t committed_byte_size, SharedSpaceType space_type) {
_vs.initialize(rs, committed_byte_size);
@@ -93,16 +94,24 @@
assert(DumpSharedSpaces, "dump time only");
_shared_rs = rs;
- // Split up and initialize the misc code and data spaces
+ size_t core_spaces_size = FileMapInfo::core_spaces_size();
size_t metadata_size = SharedReadOnlySize + SharedReadWriteSize;
- ReservedSpace shared_ro_rw = _shared_rs->first_part(metadata_size);
- ReservedSpace misc_section = _shared_rs->last_part(metadata_size);
+
+ // Split into the core and optional sections
+ ReservedSpace core_data = _shared_rs->first_part(core_spaces_size);
+ ReservedSpace optional_data = _shared_rs->last_part(core_spaces_size);
- // Now split into misc sections.
+ // The RO/RW and the misc sections
+ ReservedSpace shared_ro_rw = core_data.first_part(metadata_size);
+ ReservedSpace misc_section = core_data.last_part(metadata_size);
+
+ // Now split the misc code and misc data sections.
ReservedSpace md_rs = misc_section.first_part(SharedMiscDataSize);
ReservedSpace mc_rs = misc_section.last_part(SharedMiscDataSize);
+
_md.initialize(md_rs, SharedMiscDataSize, SharedMiscData);
- _mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscData);
+ _mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscCode);
+ _od.initialize(optional_data, metadata_size, SharedOptional);
}
// Read/write a data stream for restoring/preserving metadata pointers and
@@ -521,6 +530,7 @@
GrowableArray<Klass*> *_class_promote_order;
VirtualSpace _md_vs;
VirtualSpace _mc_vs;
+ VirtualSpace _od_vs;
GrowableArray<MemRegion> *_string_regions;
public:
@@ -598,15 +608,19 @@
remove_unshareable_in_classes();
tty->print_cr("done. ");
- // Set up the share data and shared code segments.
+ // Set up the misc data, misc code and optional data segments.
_md_vs = *MetaspaceShared::misc_data_region()->virtual_space();
_mc_vs = *MetaspaceShared::misc_code_region()->virtual_space();
+ _od_vs = *MetaspaceShared::optional_data_region()->virtual_space();
char* md_low = _md_vs.low();
char* md_top = MetaspaceShared::misc_data_region()->alloc_top();
char* md_end = _md_vs.high();
char* mc_low = _mc_vs.low();
char* mc_top = MetaspaceShared::misc_code_region()->alloc_top();
char* mc_end = _mc_vs.high();
+ char* od_low = _od_vs.low();
+ char* od_top = MetaspaceShared::optional_data_region()->alloc_top();
+ char* od_end = _od_vs.high();
// Reserve space for the list of Klass*s whose vtables are used
// for patching others as needed.
@@ -661,28 +675,32 @@
const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType);
const size_t md_alloced = md_end-md_low;
const size_t mc_alloced = mc_end-mc_low;
+ const size_t od_alloced = od_end-od_low;
const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced
- + ss_bytes;
+ + ss_bytes + od_alloced;
// Occupied size of each space.
const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType);
const size_t rw_bytes = rw_space->used_bytes_slow(Metaspace::NonClassType);
const size_t md_bytes = size_t(md_top - md_low);
const size_t mc_bytes = size_t(mc_top - mc_low);
+ const size_t od_bytes = size_t(od_top - od_low);
// Percent of total size
- const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes;
+ const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes + od_bytes;
const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0;
const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0;
const double md_t_perc = md_bytes / double(total_bytes) * 100.0;
const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0;
const double ss_t_perc = ss_bytes / double(total_bytes) * 100.0;
+ const double od_t_perc = od_bytes / double(total_bytes) * 100.0;
// Percent of fullness of each space
const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0;
const double rw_u_perc = rw_bytes / double(rw_alloced) * 100.0;
const double md_u_perc = md_bytes / double(md_alloced) * 100.0;
const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0;
+ const double od_u_perc = od_bytes / double(od_alloced) * 100.0;
const double total_u_perc = total_bytes / double(total_alloced) * 100.0;
#define fmt_space "%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT
@@ -691,6 +709,7 @@
tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, p2i(md_low));
tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, p2i(mc_low));
tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, p2i(ss_low));
+ tty->print_cr(fmt_space, "od", od_bytes, od_t_perc, od_alloced, od_u_perc, p2i(od_low));
tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]",
total_bytes, total_alloced, total_u_perc);
@@ -734,6 +753,10 @@
SharedMiscCodeSize,
true, true);
mapinfo->write_string_regions(_string_regions);
+ mapinfo->write_region(MetaspaceShared::od, _od_vs.low(),
+ pointer_delta(od_top, _od_vs.low(), sizeof(char)),
+ pointer_delta(od_end, _od_vs.low(), sizeof(char)),
+ true, false);
}
mapinfo->close();
@@ -1049,8 +1072,6 @@
// Map shared spaces at requested addresses and return if succeeded.
-// Need to keep the bounds of the ro and rw space for the Metaspace::contains
-// call, or is_in_shared_space.
bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
size_t image_alignment = mapinfo->alignment();
@@ -1068,6 +1089,7 @@
char* _rw_base = NULL;
char* _md_base = NULL;
char* _mc_base = NULL;
+ char* _od_base = NULL;
// Map each shared region
if ((_ro_base = mapinfo->map_region(ro)) != NULL &&
@@ -1078,6 +1100,8 @@
mapinfo->verify_region_checksum(md) &&
(_mc_base = mapinfo->map_region(mc)) != NULL &&
mapinfo->verify_region_checksum(mc) &&
+ (_od_base = mapinfo->map_region(od)) != NULL &&
+ mapinfo->verify_region_checksum(od) &&
(image_alignment == (size_t)max_alignment()) &&
mapinfo->validate_classpath_entry_table()) {
// Success (no need to do anything)
@@ -1089,6 +1113,7 @@
if (_rw_base != NULL) mapinfo->unmap_region(rw);
if (_md_base != NULL) mapinfo->unmap_region(md);
if (_mc_base != NULL) mapinfo->unmap_region(mc);
+ if (_od_base != NULL) mapinfo->unmap_region(od);
#ifndef _WINDOWS
// Release the entire mapped region
shared_rs.release();
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -132,6 +132,7 @@
// Used only during dumping.
static SharedMiscRegion _md;
static SharedMiscRegion _mc;
+ static SharedMiscRegion _od;
public:
enum {
vtbl_list_size = DEFAULT_VTBL_LIST_SIZE,
@@ -148,7 +149,10 @@
max_strings = 2, // max number of string regions in string space
num_non_strings = 4, // number of non-string regions
first_string = num_non_strings, // index of first string region
- n_regions = max_strings + num_non_strings // total number of regions
+ // The optional data region is the last region.
+ // Currently it only contains class file data.
+ od = max_strings + num_non_strings,
+ n_regions = od + 1 // total number of regions
};
// Accessor functions to save shared space created for metadata, which has
@@ -222,9 +226,10 @@
static int count_class(const char* classlist_file);
static void estimate_regions_size() NOT_CDS_RETURN;
- // Allocate a block of memory from the "mc" or "md" regions.
+ // Allocate a block of memory from the "mc", "md", or "od" regions.
static char* misc_code_space_alloc(size_t num_bytes) { return _mc.alloc(num_bytes); }
static char* misc_data_space_alloc(size_t num_bytes) { return _md.alloc(num_bytes); }
+ static char* optional_data_space_alloc(size_t num_bytes) { return _od.alloc(num_bytes); }
static address cds_i2i_entry_code_buffers(size_t total_size);
@@ -243,5 +248,9 @@
assert(DumpSharedSpaces, "used during dumping only");
return &_md;
}
+ static SharedMiscRegion* optional_data_region() {
+ assert(DumpSharedSpaces, "used during dumping only");
+ return &_od;
+ }
};
#endif // SHARE_VM_MEMORY_METASPACESHARED_HPP
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -41,6 +41,7 @@
#include "memory/heapInspection.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/metadataFactory.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "oops/fieldStreams.hpp"
@@ -1972,11 +1973,6 @@
m->remove_unshareable_info();
}
- // cached_class_file might be pointing to a malloc'ed buffer allocated by
- // event-based tracing code at CDS dump time. It's not usable at runtime
- // so let's clear it.
- set_cached_class_file(NULL);
-
// do array classes also.
array_klasses_do(remove_unshareable_in_class);
}
@@ -2070,6 +2066,7 @@
}
void InstanceKlass::release_C_heap_structures() {
+ assert(!this->is_shared(), "should not be called for a shared class");
// Can't release the constant pool here because the constant pool can be
// deallocated separately from the InstanceKlass for default methods and
@@ -2250,8 +2247,8 @@
// the java.base module. If a non-java.base package is erroneously placed
// in the java.base module it will be caught later when java.base
// is defined by ModuleEntryTable::verify_javabase_packages check.
- assert(ModuleEntryTable::javabase_module() != NULL, "java.base module is NULL");
- _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_module());
+ assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "java.base module is NULL");
+ _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_moduleEntry());
} else {
assert(loader_data->modules()->unnamed_module() != NULL, "unnamed module is NULL");
_package_entry = loader_data->packages()->lookup(pkg_name,
@@ -3653,6 +3650,15 @@
}
#if INCLUDE_JVMTI
+JvmtiCachedClassFileData* InstanceKlass::get_cached_class_file() {
+ if (MetaspaceShared::is_in_shared_space(_cached_class_file)) {
+ // Ignore the archived class stream data
+ return NULL;
+ } else {
+ return _cached_class_file;
+ }
+}
+
jint InstanceKlass::get_cached_class_file_len() {
return VM_RedefineClasses::get_cached_class_file_len(_cached_class_file);
}
@@ -3660,4 +3666,15 @@
unsigned char * InstanceKlass::get_cached_class_file_bytes() {
return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file);
}
+
+#if INCLUDE_CDS
+JvmtiCachedClassFileData* InstanceKlass::get_archived_class_data() {
+ assert(this->is_shared(), "class should be shared");
+ if (MetaspaceShared::is_in_shared_space(_cached_class_file)) {
+ return _cached_class_file;
+ } else {
+ return NULL;
+ }
+}
#endif
+#endif
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -783,7 +783,7 @@
void set_cached_class_file(JvmtiCachedClassFileData *data) {
_cached_class_file = data;
}
- JvmtiCachedClassFileData * get_cached_class_file() { return _cached_class_file; }
+ JvmtiCachedClassFileData * get_cached_class_file();
jint get_cached_class_file_len();
unsigned char * get_cached_class_file_bytes();
@@ -795,6 +795,13 @@
return _jvmti_cached_class_field_map;
}
+#if INCLUDE_CDS
+ void set_archived_class_data(JvmtiCachedClassFileData* data) {
+ _cached_class_file = data;
+ }
+
+ JvmtiCachedClassFileData * get_archived_class_data();
+#endif // INCLUDE_CDS
#else // INCLUDE_JVMTI
static void purge_previous_versions(InstanceKlass* ik) { return; };
--- a/hotspot/src/share/vm/oops/klass.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/klass.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -530,7 +530,7 @@
InstanceKlass* ik = (InstanceKlass*) k;
module_entry = ik->module();
} else {
- module_entry = ModuleEntryTable::javabase_module();
+ module_entry = ModuleEntryTable::javabase_moduleEntry();
}
// Obtain java.lang.reflect.Module, if available
Handle module_handle(THREAD, ((module_entry != NULL) ? JNIHandles::resolve(module_entry->module()) : (oop)NULL));
--- a/hotspot/src/share/vm/oops/oop.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/oop.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -87,6 +87,7 @@
inline narrowKlass* compressed_klass_addr();
inline void set_klass(Klass* k);
+ inline void release_set_klass(Klass* k);
// For klass field compression
inline int klass_gap() const;
--- a/hotspot/src/share/vm/oops/oop.inline.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/oop.inline.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -129,10 +129,14 @@
return &_metadata._compressed_klass;
}
+#define CHECK_SET_KLASS(k) \
+ do { \
+ assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass"); \
+ assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass"); \
+ } while (0)
+
void oopDesc::set_klass(Klass* k) {
- // since klasses are promoted no store check is needed
- assert(Universe::is_bootstrapping() || k != NULL, "must be a real Klass*");
- assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass*");
+ CHECK_SET_KLASS(k);
if (UseCompressedClassPointers) {
*compressed_klass_addr() = Klass::encode_klass_not_null(k);
} else {
@@ -140,6 +144,18 @@
}
}
+void oopDesc::release_set_klass(Klass* k) {
+ CHECK_SET_KLASS(k);
+ if (UseCompressedClassPointers) {
+ OrderAccess::release_store(compressed_klass_addr(),
+ Klass::encode_klass_not_null(k));
+ } else {
+ OrderAccess::release_store_ptr(klass_addr(), k);
+ }
+}
+
+#undef CHECK_SET_KLASS
+
int oopDesc::klass_gap() const {
return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes());
}
--- a/hotspot/src/share/vm/oops/typeArrayKlass.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -72,7 +72,7 @@
null_loader_data->add_class(ak);
// Call complete_create_array_klass after all instance variables have been initialized.
- complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_module(), CHECK_NULL);
+ complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL);
return ak;
}
@@ -347,7 +347,7 @@
// A TypeArrayKlass is an array of a primitive type, its defining module is java.base
ModuleEntry* TypeArrayKlass::module() const {
- return ModuleEntryTable::javabase_module();
+ return ModuleEntryTable::javabase_moduleEntry();
}
PackageEntry* TypeArrayKlass::package() const {
--- a/hotspot/src/share/vm/prims/unsafe.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/prims/unsafe.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -272,6 +272,31 @@
// Get/PutObject must be special-cased, since it works with handles.
+// We could be accessing the referent field in a reference
+// object. If G1 is enabled then we need to register non-null
+// referent with the SATB barrier.
+
+#if INCLUDE_ALL_GCS
+static bool is_java_lang_ref_Reference_access(oop o, jlong offset) {
+ if (offset == java_lang_ref_Reference::referent_offset && o != NULL) {
+ Klass* k = o->klass();
+ if (InstanceKlass::cast(k)->reference_type() != REF_NONE) {
+ assert(InstanceKlass::cast(k)->is_subclass_of(SystemDictionary::Reference_klass()), "sanity");
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+static void ensure_satb_referent_alive(oop o, jlong offset, oop v) {
+#if INCLUDE_ALL_GCS
+ if (UseG1GC && v != NULL && is_java_lang_ref_Reference_access(o, offset)) {
+ G1SATBCardTableModRefBS::enqueue(v);
+ }
+#endif
+}
+
// These functions allow a null base pointer with an arbitrary address.
// But if the base pointer is non-null, the offset should make some sense.
// That is, it should be in the range [0, MAX_OBJECT_SIZE].
@@ -286,34 +311,9 @@
v = *(oop*)index_oop_from_field_offset_long(p, offset);
}
- jobject ret = JNIHandles::make_local(env, v);
-
-#if INCLUDE_ALL_GCS
- // We could be accessing the referent field in a reference
- // object. If G1 is enabled then we need to register non-null
- // referent with the SATB barrier.
- if (UseG1GC) {
- bool needs_barrier = false;
+ ensure_satb_referent_alive(p, offset, v);
- if (ret != NULL) {
- if (offset == java_lang_ref_Reference::referent_offset && obj != NULL) {
- oop o = JNIHandles::resolve(obj);
- Klass* k = o->klass();
- if (InstanceKlass::cast(k)->reference_type() != REF_NONE) {
- assert(InstanceKlass::cast(k)->is_subclass_of(SystemDictionary::Reference_klass()), "sanity");
- needs_barrier = true;
- }
- }
- }
-
- if (needs_barrier) {
- oop referent = JNIHandles::resolve(ret);
- G1SATBCardTableModRefBS::enqueue(referent);
- }
- }
-#endif // INCLUDE_ALL_GCS
-
- return ret;
+ return JNIHandles::make_local(env, v);
} UNSAFE_END
UNSAFE_ENTRY(void, Unsafe_PutObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) {
@@ -344,6 +344,8 @@
(void)const_cast<oop&>(v = *(volatile oop*) addr);
}
+ ensure_satb_referent_alive(p, offset, v);
+
OrderAccess::acquire();
return JNIHandles::make_local(env, v);
} UNSAFE_END
--- a/hotspot/src/share/vm/runtime/globals.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/globals.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -1596,6 +1596,10 @@
product(bool, AlwaysPreTouch, false, \
"Force all freshly committed pages to be pre-touched") \
\
+ product(size_t, PreTouchParallelChunkSize, 1 * G, \
+ "Per-thread chunk size for parallel memory pre-touch.") \
+ range(1, SIZE_MAX / 2) \
+ \
product_pd(size_t, CMSYoungGenPerWorker, \
"The maximum size of young gen chosen by default per GC worker " \
"thread available") \
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -77,6 +77,8 @@
Mutex* DirtyCardQ_FL_lock = NULL;
Monitor* DirtyCardQ_CBL_mon = NULL;
Mutex* Shared_DirtyCardQ_lock = NULL;
+Mutex* MarkStackFreeList_lock = NULL;
+Mutex* MarkStackChunkList_lock = NULL;
Mutex* ParGCRareEvent_lock = NULL;
Mutex* DerivedPointerTableGC_lock = NULL;
Mutex* Compile_lock = NULL;
@@ -194,6 +196,9 @@
def(StringDedupQueue_lock , Monitor, leaf, true, Monitor::_safepoint_check_never);
def(StringDedupTable_lock , Mutex , leaf, true, Monitor::_safepoint_check_never);
+
+ def(MarkStackFreeList_lock , Mutex , leaf , true, Monitor::_safepoint_check_never);
+ def(MarkStackChunkList_lock , Mutex , leaf , true, Monitor::_safepoint_check_never);
}
def(ParGCRareEvent_lock , Mutex , leaf , true, Monitor::_safepoint_check_sometimes);
def(DerivedPointerTableGC_lock , Mutex, leaf, true, Monitor::_safepoint_check_never);
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -81,7 +81,8 @@
extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card
// queue shared by
// non-Java threads.
- // (see option ExplicitGCInvokesConcurrent)
+extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list.
+extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops.
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued
--- a/hotspot/src/share/vm/runtime/os.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/os.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -1705,8 +1705,8 @@
return res;
}
-void os::pretouch_memory(void* start, void* end) {
- for (volatile char *p = (char*)start; p < (char*)end; p += os::vm_page_size()) {
+void os::pretouch_memory(void* start, void* end, size_t page_size) {
+ for (volatile char *p = (char*)start; p < (char*)end; p += page_size) {
*p = 0;
}
}
--- a/hotspot/src/share/vm/runtime/os.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/os.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -324,7 +324,7 @@
// to make the OS back the memory range with actual memory.
// Current implementation may not touch the last page if unaligned addresses
// are passed.
- static void pretouch_memory(void* start, void* end);
+ static void pretouch_memory(void* start, void* end, size_t page_size = vm_page_size());
enum ProtType { MEM_PROT_NONE, MEM_PROT_READ, MEM_PROT_RW, MEM_PROT_RWX };
static bool protect_memory(char* addr, size_t bytes, ProtType prot,
--- a/hotspot/src/share/vm/utilities/debug.cpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/utilities/debug.cpp Thu Sep 29 17:23:47 2016 +0000
@@ -282,6 +282,12 @@
}
void report_out_of_shared_space(SharedSpaceType shared_space) {
+ if (shared_space == SharedOptional) {
+ // The estimated shared_optional_space size is large enough
+ // for all class bytes. It should not run out of space.
+ ShouldNotReachHere();
+ }
+
static const char* name[] = {
"shared read only space",
"shared read write space",
--- a/hotspot/src/share/vm/utilities/debug.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/utilities/debug.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -271,7 +271,8 @@
SharedReadOnly,
SharedReadWrite,
SharedMiscData,
- SharedMiscCode
+ SharedMiscCode,
+ SharedOptional
};
void report_out_of_shared_space(SharedSpaceType space_type);
--- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp Thu Sep 29 17:23:47 2016 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2012, 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
@@ -79,8 +79,8 @@
template <MEMFLAGS F> inline void HashtableBucket<F>::set_entry(BasicHashtableEntry<F>* l) {
- // Warning: Preserve store ordering. The SystemDictionary is read
- // without locks. The new SystemDictionaryEntry must be
+ // Warning: Preserve store ordering. The PackageEntryTable, ModuleEntryTable and
+ // SystemDictionary are read without locks. The new entry must be
// complete before other threads can be allowed to see it
// via a store to _buckets[index].
OrderAccess::release_store_ptr(&_entry, l);
@@ -88,8 +88,8 @@
template <MEMFLAGS F> inline BasicHashtableEntry<F>* HashtableBucket<F>::get_entry() const {
- // Warning: Preserve load ordering. The SystemDictionary is read
- // without locks. The new SystemDictionaryEntry must be
+ // Warning: Preserve load ordering. The PackageEntryTable, ModuleEntryTable and
+ // SystemDictionary are read without locks. The new entry must be
// complete before other threads can be allowed to see it
// via a store to _buckets[index].
return (BasicHashtableEntry<F>*) OrderAccess::load_ptr_acquire(&_entry);
--- a/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Thu Sep 29 16:45:09 2016 +0000
+++ b/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Thu Sep 29 17:23:47 2016 +0000
@@ -110,10 +110,6 @@
excludeTestMaxRange("OldSize");
excludeTestMaxRange("ParallelGCThreads");
- excludeTestMaxRange("CompilerThreadStackSize");
- excludeTestMaxRange("ThreadStackSize");
- excludeTestMaxRange("VMThreadStackSize");
-
/*
* Remove parameters controlling the code cache. As these
* parameters have implications on the physical memory
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/CDSTestUtils.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+// This class contains common test utilities for CDS testing
+public class CDSTestUtils {
+
+ // check result of 'dump' operation
+ public static void checkDump(OutputAnalyzer output, String... extraMatches)
+ throws Exception {
+
+ output.shouldContain("Loading classes to share");
+ output.shouldHaveExitValue(0);
+
+ for (String match : extraMatches) {
+ output.shouldContain(match);
+ }
+ }
+
+
+ // check the output for indication that mapping of the archive failed
+ public static boolean isUnableToMap(OutputAnalyzer output) {
+ String outStr = output.getOutput();
+ if ((output.getExitValue() == 1) && (
+ outStr.contains("Unable to reserve shared space at required address") ||
+ outStr.contains("Unable to map ReadOnly shared space at required address") ||
+ outStr.contains("Unable to map ReadWrite shared space at required address") ||
+ outStr.contains("Unable to map MiscData shared space at required address") ||
+ outStr.contains("Unable to map MiscCode shared space at required address") ||
+ outStr.contains("Unable to map shared string space at required address") ||
+ outStr.contains("Could not allocate metaspace at a compatible address") ||
+ outStr.contains("Unable to allocate shared string space: range is not within java heap") ))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ // check result of 'exec' operation, that is when JVM is run using the archive
+ public static void checkExec(OutputAnalyzer output, String... extraMatches) throws Exception {
+ if (isUnableToMap(output)) {
+ System.out.println("Unable to map shared archive: test did not complete; assumed PASS");
+ return;
+ }
+ output.shouldContain("sharing");
+ output.shouldHaveExitValue(0);
+
+ for (String match : extraMatches) {
+ output.shouldContain(match);
+ }
+ }
+
+
+ // get the file object for the test artifact
+ private static File getTestArtifactFile(String prefix, String name) {
+ File dir = new File(System.getProperty("test.classes", "."));
+ return new File(dir, prefix + name);
+ }
+
+
+ // create file containing the specified class list
+ public static File makeClassList(String testCaseName, String classes[])
+ throws Exception {
+
+ File classList = getTestArtifactFile(testCaseName, "test.classlist");
+ FileOutputStream fos = new FileOutputStream(classList);
+ PrintStream ps = new PrintStream(fos);
+
+ addToClassList(ps, classes);
+
+ ps.close();
+ fos.close();
+
+ return classList;
+ }
+
+
+ private static void addToClassList(PrintStream ps, String classes[])
+ throws IOException
+ {
+ if (classes != null) {
+ for (String s : classes) {
+ ps.println(s);
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Implementor.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,37 @@
+/*
+ * 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 Implementor implements Interface {
+ public static void main(String[] args) {
+ System.out.println("Implementor: entering main()");
+ test();
+ }
+
+ public static void test() {
+ // from interface
+ (new Implementor()).printString();
+ // from implementor
+ System.out.println(TransformUtil.ChildCheckPattern +
+ TransformUtil.BeforePattern);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Interface.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,31 @@
+/*
+ * 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 interface Interface {
+ public static final String stringToBeTransformed =
+ TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern;
+
+ default void printString() {
+ System.out.println(stringToBeTransformed);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SubClass.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,40 @@
+/*
+ * 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 SubClass extends SuperClazz {
+ public static void main(String[] args) {
+ System.out.println("SubClass: entering main()");
+ test();
+ }
+
+ public static void test() {
+ // The line below will be used to check for successful class transformation
+ System.out.println(TransformUtil.ChildCheckPattern +
+ TransformUtil.BeforePattern);
+ (new SubClass()).callParent();
+ }
+
+ private void callParent() {
+ super.testParent();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SuperClazz.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,32 @@
+/*
+ * 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 SuperClazz {
+ public static void testParent() {
+ System.out.println("SuperClazz: entering testParent()");
+
+ // The line below will be used to check for successful class transformation
+ System.out.println(TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TestEntry.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,45 @@
+/*
+ * 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 Entry - a single entry in a test table
+// that defines a test case
+// See TransformRelatedClasses.java for more details
+public class TestEntry {
+ int testCaseId;
+ boolean transformParent;
+ boolean transformChild;
+ boolean isParentExpectedShared;
+ boolean isChildExpectedShared;
+
+ public TestEntry(int testCaseId,
+ boolean transformParent, boolean transformChild,
+ boolean isParentExpectedShared, boolean isChildExpectedShared) {
+ this.testCaseId = testCaseId;
+ this.transformParent = transformParent;
+ this.transformChild = transformChild;
+ this.isParentExpectedShared = isParentExpectedShared;
+ this.isChildExpectedShared = isChildExpectedShared;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * @summary Exercise initial transformation (ClassFileLoadHook)
+ * with CDS with Interface/Implementor pair
+ * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ * jdk.jartool/sun.tools.jar
+ * java.management
+ * java.instrument
+ * @build TransformUtil TransformerAgent Interface Implementor
+ * @run main/othervm TransformRelatedClasses Interface Implementor
+ */
+
+// Clarification on @requires declarations:
+// CDS is not supported w/o the use of Compressed OOPs
+// JVMTI's ClassFileLoadHook is not supported under minimal VM
+
+// This test class uses TransformRelatedClasses to do its work.
+// The goal of this test is to exercise transformation of related interface
+// and its implementor in combination with CDS.
+// The transformation is done via ClassFileLoadHook mechanism.
+// Both superclass and subclass reside in the shared archive.
+// The test consists of 4 test cases where transformation is applied
+// to an interface and an implementor in a combinatorial manner.
+// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformRelatedClasses.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+
+// This is the main test class for testing transformation of related classes
+// in combination with CDS, to ensure these features work well together.
+// The relationships that can be tested using this test class are:
+// superclass/subclass, and interface/implementor relationships.
+//
+// The test uses combinatorial approach.
+// For details on test table and test cases see main() method in this class.
+//
+// This test consists of multiple classes for better flexibility and reuse,
+// and also relies on certain common utility code.
+// Here are the details on the structure of the test
+//
+// Structure of the test:
+// TransformRelatedClasses -- common main test driver
+// The TransformRelatedClasses is invoked from test driver classes:
+// TransformInterfaceAndImplementor, TransformSuperAndSubClasses
+// It is responsible for preparing test artifacts (test jar, agent jar
+// and the shared archive), running test cases and checking the results.
+// The following test classes below are launched in a sub-process with use
+// of shared archive:
+// SuperClazz, SubClass -- super/sub class pair under test
+// Interface, Implementor -- classes under test
+// This test will transform these classes, based on the test case data,
+// by changing a predefined unique string in each class.
+// For more details, see the test classes' code and comments.
+//
+// Other related classes:
+// TestEntry - a class representing a single test case, as test entry in the table
+// TransformTestCommon - common methods for transformation test cases
+//
+// Other utility/helper classes and files used in this test:
+// TransformerAgent - an agent that is used when JVM-under-test is executed
+// to transform specific strings inside specified classes
+// TransformerAgent.mf - accompanies transformer agent
+// CDSTestUtils - Test Utilities common to all CDS tests
+
+import java.io.File;
+import java.util.ArrayList;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+
+public class TransformRelatedClasses {
+ static final String archiveName = "./TransformRelatedClasses.jsa";
+ static String agentClasses[] = {
+ "TransformerAgent",
+ "TransformerAgent$SimpleTransformer",
+ "TransformUtil"
+ };
+
+ String parent;
+ String child;
+ String[] testClasses = new String[2];
+ String[] testNames = new String[2];
+ String testJar;
+ String agentJar;
+
+
+ private static void log(String msg) {
+ System.out.println("TransformRelatedClasses: " + msg);
+ }
+
+
+ // This class is intended to test 2 parent-child relationships:
+ // 1. Base Class (parent) and Derived Class (child)
+ // 2. Interface (parent) and Implementor (child)
+ // Parameters to main(): parent, child
+ public static void main(String args[]) throws Exception {
+ TransformRelatedClasses test = new TransformRelatedClasses(args[0], args[1]);
+ test.prepare();
+
+ // Test Table
+ // TestEntry: (testCaseId, transformParent, tranformChild,
+ // isParentExpectedShared, isChildExpectedShared)
+ ArrayList<TestEntry> testTable = new ArrayList<>();
+
+ // base case - no tranformation - all expected to be shared
+ testTable.add(new TestEntry(0, false, false, true, true));
+
+ // transform parent only - both parent and child should not be shared
+ testTable.add(new TestEntry(1, true, false, false, false));
+
+ // transform parent and child - both parent and child should not be shared
+ testTable.add(new TestEntry(2, true, true, false, false));
+
+ // transform child only - parent should still be shared, but not child
+ testTable.add(new TestEntry(3, false, true, true, false));
+
+ // run the tests
+ for (TestEntry entry : testTable) {
+ test.runTest(entry);
+ }
+ }
+
+
+ public TransformRelatedClasses(String parent, String child) {
+ log("Constructor: parent = " + parent + ", child = " + child);
+ this.parent = parent;
+ this.child = child;
+ testClasses[0] = parent;
+ testClasses[1] = child;
+ testNames[0] = parent.replace('.', '/');
+ testNames[1] = child.replace('.', '/');
+ }
+
+
+ // same test jar and archive can be used for all test cases
+ private void prepare() throws Exception {
+ // create agent jar
+ // Agent is the same for all test cases
+ String pathToManifest = "../../../../testlibrary/jvmti/TransformerAgent.mf";
+ agentJar = ClassFileInstaller.writeJar("TransformerAgent.jar",
+ ClassFileInstaller.Manifest.fromSourceFile(pathToManifest),
+ agentClasses);
+
+ // create a test jar
+ testJar =
+ ClassFileInstaller.writeJar(parent + "-" + child + ".jar",
+ testClasses);
+
+ // create an archive
+ File classList = CDSTestUtils.makeClassList("transform-" + parent,
+ testNames);
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+ "-Xbootclasspath/a:" + testJar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:ExtraSharedClassListFile=" +
+ classList.getPath(),
+ "-XX:SharedArchiveFile=" + archiveName,
+ "-XX:+PrintSharedSpaces",
+ "-Xshare:dump");
+ OutputAnalyzer out = new OutputAnalyzer(pb.start());
+ CDSTestUtils.checkDump(out);
+ }
+
+
+ private void runTest(TestEntry entry) throws Exception {
+ log("runTest(): testCaseId = " + entry.testCaseId);
+
+ // execute with archive
+ String agentParam = "-javaagent:" + agentJar + "=" +
+ TransformTestCommon.getAgentParams(entry, parent, child);
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+ "-Xbootclasspath/a:" + testJar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:SharedArchiveFile=" + archiveName,
+ "-Xlog:class+load=info",
+ "-Xshare:on", "-showversion",
+ agentParam, child);
+ OutputAnalyzer out = new OutputAnalyzer(pb.start());
+
+ TransformTestCommon.checkResults(entry, out, parent, child);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * @summary Exercise initial transformation (ClassFileLoadHook)
+ * with CDS with SubClass and SuperClass
+ * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ * jdk.jartool/sun.tools.jar
+ * java.management
+ * java.instrument
+ * @build TransformUtil TransformerAgent SubClass SuperClazz
+ * @run main/othervm TransformRelatedClasses SuperClazz SubClass
+*/
+
+// Clarification on @requires declarations:
+// CDS is not supported w/o the use of Compressed OOPs
+// JVMTI's ClassFileLoadHook is not supported under minimal VM
+
+// This test class uses TransformRelatedClasses to do its work.
+// The goal of this test is to exercise transformation of related superclass
+// and subclass in combination with CDS.
+// The transformation is done via ClassFileLoadHook mechanism.
+// Both superclass and subclass reside in the shared archive.
+// The test consists of 4 test cases where transformation is applied
+// to a parent and child in combinatorial manner.
+// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,52 @@
+/*
+ * 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
+ * @summary Exercise initial transformation (ClassFileLoadHook)
+ * with CDS with SubClass and SuperClass, each lives in own separate package
+ * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ * jdk.jartool/sun.tools.jar
+ * java.management
+ * java.instrument
+ * @build TransformUtil TransformerAgent SubClass SuperClazz
+ * @compile myPkg2/SubClass.java myPkg1/SuperClazz.java
+ * @run main/othervm TransformRelatedClasses myPkg1.SuperClazz myPkg2.SubClass
+*/
+
+// Clarification on @requires declarations:
+// CDS is not supported w/o the use of Compressed OOPs
+// JVMTI's ClassFileLoadHook is not supported under minimal VM
+
+// This test class uses TransformRelatedClasses to do its work.
+// The goal of this test is to exercise transformation of related superclass
+// and subclass in combination with CDS; each class lives in its own package.
+// The transformation is done via ClassFileLoadHook mechanism.
+// Both superclass and subclass reside in the shared archive.
+// The test consists of 4 test cases where transformation is applied
+// to a parent and child in combinatorial manner.
+// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+// This class contains methods common to all transformation test cases
+public class TransformTestCommon {
+
+ // get parameters to an agent depending on the test case
+ // these parameters will instruct the agent which classes should be
+ // transformed
+ public static String getAgentParams(TestEntry entry,
+ String parent, String child) {
+
+ if (entry.transformParent && entry.transformChild)
+ return parent + "," + child;
+ if (entry.transformParent)
+ return parent;
+ if (entry.transformChild)
+ return child;
+
+ return "";
+ }
+
+
+ private static void checkTransformationResults(TestEntry entry,
+ OutputAnalyzer out)
+ throws Exception {
+
+ if (entry.transformParent)
+ out.shouldContain(TransformUtil.ParentCheckPattern +
+ TransformUtil.AfterPattern);
+
+ if (entry.transformChild)
+ out.shouldContain(TransformUtil.ChildCheckPattern +
+ TransformUtil.AfterPattern);
+ }
+
+
+ private static void checkSharingByClass(TestEntry entry, OutputAnalyzer out,
+ String parent, String child)
+ throws Exception {
+
+ String parentSharedMatch = parent + " source: shared objects file";
+ String childSharedMatch = child + " source: shared objects file";
+
+ if (entry.isParentExpectedShared)
+ out.shouldContain(parentSharedMatch);
+ else
+ out.shouldNotContain(parentSharedMatch);
+
+ if (entry.isChildExpectedShared)
+ out.shouldContain(childSharedMatch);
+ else
+ out.shouldNotContain(childSharedMatch);
+ }
+
+
+ // Both parent and child classes should be passed to ClassFileTransformer.transform()
+ // exactly once.
+ private static void checkTransformationCounts(TestEntry entry, OutputAnalyzer out,
+ String parent, String child)
+ throws Exception {
+
+ String patternBase = "TransformerAgent: SimpleTransformer called for: ";
+
+ out.shouldContain(patternBase + child + "@1");
+ out.shouldContain(patternBase + parent + "@1");
+
+ out.shouldNotContain(patternBase + child + "@2");
+ out.shouldNotContain(patternBase + parent + "@2");
+ }
+
+
+ public static void checkResults(TestEntry entry, OutputAnalyzer out,
+ String parent, String child)
+ throws Exception {
+
+ // If we were not able to map an archive,
+ // then do not perform other checks, since
+ // there was no sharing at all
+ if (CDSTestUtils.isUnableToMap(out))
+ return;
+
+ String childVmName = child.replace('.', '/');
+ String parentVmName = parent.replace('.', '/');
+
+ CDSTestUtils.checkExec(out);
+ checkTransformationCounts(entry, out, parentVmName, childVmName);
+ checkTransformationResults(entry, out);
+ checkSharingByClass(entry, out, parent, child);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg1/SuperClazz.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,33 @@
+/*
+ * 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 myPkg1;
+
+public class SuperClazz {
+ public static void testParent() {
+ System.out.println("SuperClazz: entering testParent()");
+
+ // The line below will be used to check for successful class transformation
+ System.out.println("parent-transform-check: this-should-be-transformed");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg2/SubClass.java Thu Sep 29 17:23:47 2016 +0000
@@ -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 myPkg2;
+
+import myPkg1.SuperClazz;
+
+public class SubClass extends SuperClazz {
+ public static void main(String[] args) {
+ System.out.println("SubClass: entering main()");
+ test();
+ }
+
+ public static void test() {
+ // The line below will be used to check for successful class transformation
+ System.out.println("child-transform-check: this-should-be-transformed");
+ (new SubClass()).callParent();
+
+ // Get the system packages, which should contain myPkg1 and myPkag2
+ Package[] pkgs = Package.getPackages();
+ for (int i = 0; i < pkgs.length; i++) {
+ if (pkgs[i].getName().equals("myPkg1")) {
+ for (int j = 0; j < pkgs.length; j++) {
+ if (pkgs[j].getName().equals("myPkg2")) {
+ return; // found myPkg1 & myPkg1
+ }
+ }
+ }
+ }
+ throw new RuntimeException("Missing system package");
+ }
+
+ private void callParent() {
+ super.testParent();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/jvmti/TransformUtil.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,75 @@
+/*
+ * 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 TransformUtil {
+ public static final String BeforePattern = "this-should-be-transformed";
+ public static final String AfterPattern = "this-has-been--transformed";
+ public static final String ParentCheckPattern = "parent-transform-check: ";
+ public static final String ChildCheckPattern = "child-transform-check: ";
+
+ /**
+ * @return the number of occurrences of the <code>from</code> string that
+ * have been replaced.
+ */
+ public static int replace(byte buff[], String from, String to) {
+ if (to.length() != from.length()) {
+ throw new RuntimeException("bad strings");
+ }
+ byte f[] = asciibytes(from);
+ byte t[] = asciibytes(to);
+ byte f0 = f[0];
+
+ int numReplaced = 0;
+ int max = buff.length - f.length;
+ for (int i = 0; i < max; ) {
+ if (buff[i] == f0 && replace(buff, f, t, i)) {
+ i += f.length;
+ numReplaced++;
+ } else {
+ i++;
+ }
+ }
+ return numReplaced;
+ }
+
+ public static boolean replace(byte buff[], byte f[], byte t[], int i) {
+ for (int x = 0; x < f.length; x++) {
+ if (buff[x+i] != f[x]) {
+ return false;
+ }
+ }
+ for (int x = 0; x < f.length; x++) {
+ buff[x+i] = t[x];
+ }
+ return true;
+ }
+
+ static byte[] asciibytes(String s) {
+ byte b[] = new byte[s.length()];
+ for (int i = 0; i < b.length; i++) {
+ b[i] = (byte)s.charAt(i);
+ }
+ return b;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.java Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+import java.util.HashMap;
+
+// This is a test utility class used to transform
+// specified classes via initial transformation (ClassFileLoadHook).
+// Names of classes to be transformed are supplied as arguments,
+// the phrase to be transformed is a hard-coded predefined
+// fairly unique phrase.
+
+public class TransformerAgent {
+ private static String[] classesToTransform;
+
+
+ private static void log(String msg) {
+ System.out.println("TransformerAgent: " + msg);
+ }
+
+
+ // arguments are comma-separated list of classes to transform
+ public static void premain(String agentArguments, Instrumentation instrumentation) {
+ log("premain() is called, arguments = " + agentArguments);
+ classesToTransform = agentArguments.split(",");
+ instrumentation.addTransformer(new SimpleTransformer(), /*canRetransform=*/true);
+ }
+
+
+ public static void agentmain(String args, Instrumentation inst) throws Exception {
+ log("agentmain() is called");
+ premain(args, inst);
+ }
+
+
+ static class SimpleTransformer implements ClassFileTransformer {
+ public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
+ ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
+
+ log("SimpleTransformer called for: " + name + "@" + incrCounter(name));
+ if (!shouldTransform(name))
+ return null;
+
+ log("transforming: class name = " + name);
+ int nrOfReplacements = TransformUtil.replace(buffer, TransformUtil.BeforePattern,
+ TransformUtil.AfterPattern);
+ log("replaced the string, nrOfReplacements = " + nrOfReplacements);
+ return buffer;
+ }
+
+ // Check class name pattern, since test should only transform certain classes
+ private static boolean shouldTransform(String name) {
+ for (String match : classesToTransform) {
+ if (name.matches(match)) {
+ log("shouldTransform: match-found, match = " + match);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+
+ static HashMap<String, Integer> counterMap = new HashMap<>();
+
+ static Integer incrCounter(String className) {
+ Integer i = counterMap.get(className);
+ if (i == null) {
+ i = new Integer(1);
+ } else {
+ i = new Integer(i.intValue() + 1);
+ }
+ counterMap.put(className, i);
+ return i;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.mf Thu Sep 29 17:23:47 2016 +0000
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Premain-Class: TransformerAgent
+Agent-Class: TransformerAgent
+Can-Retransform-Classes: true
+Can-Redefine-Classes: false