8015237: Parallelize string table scanning during strong root processing
Summary: Parallelize the scanning of the intern string table by having each GC worker claim a given number of buckets. Changes were also reviewed by Per Liden <per.liden@oracle.com>.
Reviewed-by: tschatzl, stefank, twisti
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Fri Jun 14 08:02:32 2013 +0200
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Jun 18 12:31:07 2013 -0700
@@ -598,6 +598,8 @@
bool StringTable::_needs_rehashing = false;
+volatile int StringTable::_parallel_claimed_idx = 0;
+
// Pick hashing algorithm
unsigned int StringTable::hash_string(const jchar* s, int len) {
return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) :
@@ -761,8 +763,18 @@
}
}
-void StringTable::oops_do(OopClosure* f) {
- for (int i = 0; i < the_table()->table_size(); ++i) {
+void StringTable::buckets_do(OopClosure* f, int start_idx, int end_idx) {
+ const int limit = the_table()->table_size();
+
+ assert(0 <= start_idx && start_idx <= limit,
+ err_msg("start_idx (" INT32_FORMAT ") oob?", start_idx));
+ assert(0 <= end_idx && end_idx <= limit,
+ err_msg("end_idx (" INT32_FORMAT ") oob?", end_idx));
+ assert(start_idx <= end_idx,
+ err_msg("Ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT,
+ start_idx, end_idx));
+
+ for (int i = start_idx; i < end_idx; i += 1) {
HashtableEntry<oop, mtSymbol>* entry = the_table()->bucket(i);
while (entry != NULL) {
assert(!entry->is_shared(), "CDS not used for the StringTable");
@@ -774,6 +786,27 @@
}
}
+void StringTable::oops_do(OopClosure* f) {
+ buckets_do(f, 0, the_table()->table_size());
+}
+
+void StringTable::possibly_parallel_oops_do(OopClosure* f) {
+ const int ClaimChunkSize = 32;
+ const int limit = the_table()->table_size();
+
+ for (;;) {
+ // Grab next set of buckets to scan
+ int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize;
+ if (start_idx >= limit) {
+ // End of table
+ break;
+ }
+
+ int end_idx = MIN2(limit, start_idx + ClaimChunkSize);
+ buckets_do(f, start_idx, end_idx);
+ }
+}
+
void StringTable::verify() {
for (int i = 0; i < the_table()->table_size(); ++i) {
HashtableEntry<oop, mtSymbol>* p = the_table()->bucket(i);
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Fri Jun 14 08:02:32 2013 +0200
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Tue Jun 18 12:31:07 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -246,12 +246,19 @@
// Set if one bucket is out of balance due to hash algorithm deficiency
static bool _needs_rehashing;
+ // Claimed high water mark for parallel chunked scanning
+ static volatile int _parallel_claimed_idx;
+
static oop intern(Handle string_or_null, jchar* chars, int length, TRAPS);
oop basic_add(int index, Handle string_or_null, jchar* name, int len,
unsigned int hashValue, TRAPS);
oop lookup(int index, jchar* chars, int length, unsigned int hashValue);
+ // Apply the give oop closure to the entries to the buckets
+ // in the range [start_idx, end_idx).
+ static void buckets_do(OopClosure* f, int start_idx, int end_idx);
+
StringTable() : Hashtable<oop, mtSymbol>((int)StringTableSize,
sizeof (HashtableEntry<oop, mtSymbol>)) {}
@@ -277,9 +284,12 @@
unlink_or_oops_do(cl, NULL);
}
- // Invoke "f->do_oop" on the locations of all oops in the table.
+ // Serially invoke "f->do_oop" on the locations of all oops in the table.
static void oops_do(OopClosure* f);
+ // Possibly parallel version of the above
+ static void possibly_parallel_oops_do(OopClosure* f);
+
// Hashing algorithm, used as the hash value used by the
// StringTable for bucket selection and comparison (stored in the
// HashtableEntry structures). This is used in the String.intern() method.
@@ -315,5 +325,8 @@
// Rehash the symbol table if it gets out of balance
static void rehash_table();
static bool needs_rehashing() { return _needs_rehashing; }
+
+ // Parallel chunked scanning
+ static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; }
};
#endif // SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp Fri Jun 14 08:02:32 2013 +0200
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp Tue Jun 18 12:31:07 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,7 +47,6 @@
SH_PS_SystemDictionary_oops_do,
SH_PS_ClassLoaderDataGraph_oops_do,
SH_PS_jvmti_oops_do,
- SH_PS_StringTable_oops_do,
SH_PS_CodeCache_oops_do,
// Leave this one last.
SH_PS_NumElements
@@ -127,6 +126,8 @@
{
if (_active) {
outer->change_strong_roots_parity();
+ // Zero the claimed high water mark in the StringTable
+ StringTable::clear_parallel_claimed_index();
}
}
@@ -154,14 +155,16 @@
// Global (strong) JNI handles
if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do))
JNIHandles::oops_do(roots);
+
// All threads execute this; the individual threads are task groups.
CLDToOopClosure roots_from_clds(roots);
CLDToOopClosure* roots_from_clds_p = (is_scavenging ? NULL : &roots_from_clds);
- if (ParallelGCThreads > 0) {
- Threads::possibly_parallel_oops_do(roots, roots_from_clds_p ,code_roots);
+ if (CollectedHeap::use_parallel_gc_threads()) {
+ Threads::possibly_parallel_oops_do(roots, roots_from_clds_p, code_roots);
} else {
Threads::oops_do(roots, roots_from_clds_p, code_roots);
}
+
if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do))
ObjectSynchronizer::oops_do(roots);
if (!_process_strong_tasks->is_task_claimed(SH_PS_FlatProfiler_oops_do))
@@ -189,8 +192,12 @@
}
}
- if (!_process_strong_tasks->is_task_claimed(SH_PS_StringTable_oops_do)) {
- if (so & SO_Strings) {
+ // All threads execute the following. A specific chunk of buckets
+ // from the StringTable are the individual tasks.
+ if (so & SO_Strings) {
+ if (CollectedHeap::use_parallel_gc_threads()) {
+ StringTable::possibly_parallel_oops_do(roots);
+ } else {
StringTable::oops_do(roots);
}
}