# HG changeset patch # User coleenp # Date 1340674415 14400 # Node ID c146b608d91f5c7d0506feaf83ceae3b31753d9d # Parent e963ab7b3a6fc20e6728ee2a22a26e480d7636f0 7178670: runtime/7158800/BadUtf8.java fails in SymbolTable::rehash_table Summary: Cannot delete _buckets and HashtableEntries in shared space (CDS) Reviewed-by: acorn, kvn, dlong, dcubed, kamg diff -r e963ab7b3a6f -r c146b608d91f hotspot/src/share/vm/classfile/symbolTable.cpp --- a/hotspot/src/share/vm/classfile/symbolTable.cpp Fri Jun 22 15:39:16 2012 -0700 +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Mon Jun 25 21:33:35 2012 -0400 @@ -46,11 +46,8 @@ jint SymbolTable::_seed = 0; Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS) { - // Don't allow symbols to be created which cannot fit in a Symbol*. - if (len > Symbol::max_length()) { - THROW_MSG_0(vmSymbols::java_lang_InternalError(), - "name is too long to represent"); - } + assert (len <= Symbol::max_length(), "should be checked by caller"); + Symbol* sym; // Allocate symbols in the C heap when dumping shared spaces in case there // are temporary symbols we can remove. @@ -95,9 +92,14 @@ int total = 0; size_t memory_total = 0; for (int i = 0; i < the_table()->table_size(); ++i) { - for (HashtableEntry** p = the_table()->bucket_addr(i); *p != NULL; ) { - HashtableEntry* entry = *p; - if (entry->is_shared()) { + HashtableEntry** p = the_table()->bucket_addr(i); + HashtableEntry* entry = the_table()->bucket(i); + while (entry != NULL) { + // Shared entries are normally at the end of the bucket and if we run into + // a shared entry, then there is nothing more to remove. However, if we + // have rehashed the table, then the shared entries are no longer at the + // end of the bucket. + if (entry->is_shared() && !use_alternate_hashcode()) { break; } Symbol* s = entry->literal(); @@ -106,6 +108,7 @@ assert(s != NULL, "just checking"); // If reference count is zero, remove. if (s->refcount() == 0) { + assert(!entry->is_shared(), "shared entries should be kept live"); delete s; removed++; *p = entry->next(); @@ -113,6 +116,8 @@ } else { p = entry->next_addr(); } + // get next entry + entry = (HashtableEntry*)HashtableEntry::make_ptr(*p); } } symbols_removed += removed; @@ -135,7 +140,8 @@ // with the existing strings. Set flag to use the alternate hash code afterwards. void SymbolTable::rehash_table() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - assert(!DumpSharedSpaces, "this should never happen with -Xshare:dump"); + // This should never happen with -Xshare:dump but it might in testing mode. + if (DumpSharedSpaces) return; // Create a new symbol table SymbolTable* new_table = new SymbolTable(); @@ -176,12 +182,11 @@ return NULL; } -// Pick hashing algorithm, but return value already given if not using a new -// hash algorithm. -unsigned int SymbolTable::hash_symbol(const char* s, int len, unsigned int hashValue) { +// Pick hashing algorithm. +unsigned int SymbolTable::hash_symbol(const char* s, int len) { return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), (const jbyte*)s, len) : - (hashValue != 0 ? hashValue : java_lang_String::to_hash(s, len)); + java_lang_String::to_hash(s, len); } @@ -201,6 +206,9 @@ // Found if (s != NULL) return s; + // Grab SymbolTable_lock first. + MutexLocker ml(SymbolTable_lock, THREAD); + // Otherwise, add to symbol to table return the_table()->basic_add(index, (u1*)name, len, hashValue, true, CHECK_NULL); } @@ -238,6 +246,9 @@ // We can't include the code in No_Safepoint_Verifier because of the // ResourceMark. + // Grab SymbolTable_lock first. + MutexLocker ml(SymbolTable_lock, THREAD); + return the_table()->basic_add(index, (u1*)buffer, len, hashValue, true, CHECK_NULL); } @@ -306,6 +317,9 @@ int names_count, const char** names, int* lengths, int* cp_indices, unsigned int* hashValues, TRAPS) { + // Grab SymbolTable_lock first. + MutexLocker ml(SymbolTable_lock, THREAD); + SymbolTable* table = the_table(); bool added = table->basic_add(class_loader, cp, names_count, names, lengths, cp_indices, hashValues, CHECK); @@ -326,22 +340,39 @@ if (result != NULL) { return result; } + // Grab SymbolTable_lock first. + MutexLocker ml(SymbolTable_lock, THREAD); + SymbolTable* table = the_table(); int index = table->hash_to_index(hash); return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD); } -Symbol* SymbolTable::basic_add(int index, u1 *name, int len, +Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len, unsigned int hashValue_arg, bool c_heap, TRAPS) { assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), "proposed name of symbol must be stable"); - // Grab SymbolTable_lock first. - MutexLocker ml(SymbolTable_lock, THREAD); + // Don't allow symbols to be created which cannot fit in a Symbol*. + if (len > Symbol::max_length()) { + THROW_MSG_0(vmSymbols::java_lang_InternalError(), + "name is too long to represent"); + } + + // Cannot hit a safepoint in this function because the "this" pointer can move. + No_Safepoint_Verifier nsv; // Check if the symbol table has been rehashed, if so, need to recalculate - // the hash value. - unsigned int hashValue = hash_symbol((const char*)name, len, hashValue_arg); + // the hash value and index. + unsigned int hashValue; + int index; + if (use_alternate_hashcode()) { + hashValue = hash_symbol((const char*)name, len); + index = hash_to_index(hashValue); + } else { + hashValue = hashValue_arg; + index = index_arg; + } // Since look-up was done lock-free, we need to check if another // thread beat us in the race to insert the symbol. @@ -377,13 +408,18 @@ } } - // Hold SymbolTable_lock through the symbol creation - MutexLocker ml(SymbolTable_lock, THREAD); + // Cannot hit a safepoint in this function because the "this" pointer can move. + No_Safepoint_Verifier nsv; for (int i=0; iis_in_reserved(name) || GC_locker::is_active(), - "proposed name of symbol must be stable"); - - Handle string; - // try to reuse the string if possible - if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()->is_perm())) { - string = string_or_null; - } else { - string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL); - } - - // Allocation must be done before grapping the SymbolTable_lock lock - MutexLocker ml(StringTable_lock, THREAD); assert(java_lang_String::equals(string(), name, len), "string must be properly initialized"); + // Cannot hit a safepoint in this function because the "this" pointer can move. + No_Safepoint_Verifier nsv; // Check if the symbol table has been rehashed, if so, need to recalculate - // the hash value before second lookup. - unsigned int hashValue = hash_string(name, len, hashValue_arg); + // the hash value and index before second lookup. + unsigned int hashValue; + int index; + if (use_alternate_hashcode()) { + hashValue = hash_string(name, len); + index = hash_to_index(hashValue); + } else { + hashValue = hashValue_arg; + index = index_arg; + } // Since look-up was done lock-free, we need to check if another // thread beat us in the race to insert the symbol. @@ -664,13 +696,29 @@ int len, TRAPS) { unsigned int hashValue = hash_string(name, len); int index = the_table()->hash_to_index(hashValue); - oop string = the_table()->lookup(index, name, len, hashValue); + oop found_string = the_table()->lookup(index, name, len, hashValue); // Found - if (string != NULL) return string; + if (found_string != NULL) return found_string; + + debug_only(StableMemoryChecker smc(name, len * sizeof(name[0]))); + assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), + "proposed name of symbol must be stable"); + + Handle string; + // try to reuse the string if possible + if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()->is_perm())) { + string = string_or_null; + } else { + string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL); + } + + // Grab the StringTable_lock before getting the_table() because it could + // change at safepoint. + MutexLocker ml(StringTable_lock, THREAD); // Otherwise, add to symbol to table - return the_table()->basic_add(index, string_or_null, name, len, + return the_table()->basic_add(index, string, name, len, hashValue, CHECK_NULL); } @@ -713,18 +761,24 @@ // entries at a safepoint. assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); for (int i = 0; i < the_table()->table_size(); ++i) { - for (HashtableEntry** p = the_table()->bucket_addr(i); *p != NULL; ) { - HashtableEntry* entry = *p; - if (entry->is_shared()) { + HashtableEntry** p = the_table()->bucket_addr(i); + HashtableEntry* entry = the_table()->bucket(i); + while (entry != NULL) { + // Shared entries are normally at the end of the bucket and if we run into + // a shared entry, then there is nothing more to remove. However, if we + // have rehashed the table, then the shared entries are no longer at the + // end of the bucket. + if (entry->is_shared() && !use_alternate_hashcode()) { break; } assert(entry->literal() != NULL, "just checking"); - if (is_alive->do_object_b(entry->literal())) { + if (entry->is_shared() || is_alive->do_object_b(entry->literal())) { p = entry->next_addr(); } else { *p = entry->next(); the_table()->free_entry(entry); } + entry = (HashtableEntry*)HashtableEntry::make_ptr(*p); } } } @@ -795,7 +849,8 @@ // with the existing strings. Set flag to use the alternate hash code afterwards. void StringTable::rehash_table() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - assert(!DumpSharedSpaces, "this should never happen with -Xshare:dump"); + // This should never happen with -Xshare:dump but it might in testing mode. + if (DumpSharedSpaces) return; StringTable* new_table = new StringTable(); // Initialize new global seed for hashing. diff -r e963ab7b3a6f -r c146b608d91f hotspot/src/share/vm/classfile/symbolTable.hpp --- a/hotspot/src/share/vm/classfile/symbolTable.hpp Fri Jun 22 15:39:16 2012 -0700 +++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Mon Jun 25 21:33:35 2012 -0400 @@ -156,7 +156,7 @@ initialize_symbols(); } - static unsigned int hash_symbol(const char* s, int len, unsigned int hashValue = 0); + static unsigned int hash_symbol(const char* s, int len); static Symbol* lookup(const char* name, int len, TRAPS); // lookup only, won't add. Also calculate hash. @@ -294,7 +294,7 @@ // 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. - static unsigned int hash_string(const jchar* s, int len, unsigned int hashValue = 0); + static unsigned int hash_string(const jchar* s, int len); // Internal test. static void test_alt_hash() PRODUCT_RETURN; diff -r e963ab7b3a6f -r c146b608d91f hotspot/src/share/vm/utilities/hashtable.cpp --- a/hotspot/src/share/vm/utilities/hashtable.cpp Fri Jun 22 15:39:16 2012 -0700 +++ b/hotspot/src/share/vm/utilities/hashtable.cpp Mon Jun 25 21:33:35 2012 -0400 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "memory/allocation.inline.hpp" +#include "memory/filemap.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "runtime/safepoint.hpp" @@ -119,8 +120,16 @@ // Get a new index relative to the new table (can also change size) int index = new_table->hash_to_index(hashValue); p->set_hash(hashValue); + // Keep the shared bit in the Hashtable entry to indicate that this entry + // can't be deleted. The shared bit is the LSB in the _next field so + // walking the hashtable past these entries requires + // BasicHashtableEntry::make_ptr() call. + bool keep_shared = p->is_shared(); unlink_entry(p); new_table->add_entry(index, p); + if (keep_shared) { + p->set_shared(); + } p = next; } } @@ -135,6 +144,19 @@ free_buckets(); } +void BasicHashtable::free_buckets() { + if (NULL != _buckets) { + // Don't delete the buckets in the shared space. They aren't + // allocated by os::malloc + if (!UseSharedSpaces || + !FileMapInfo::current_info()->is_in_shared_space(_buckets)) { + FREE_C_HEAP_ARRAY(HashtableBucket, _buckets); + } + _buckets = NULL; + } +} + + // Reverse the order of elements in the hash buckets. void BasicHashtable::reverse() { diff -r e963ab7b3a6f -r c146b608d91f hotspot/src/share/vm/utilities/hashtable.hpp --- a/hotspot/src/share/vm/utilities/hashtable.hpp Fri Jun 22 15:39:16 2012 -0700 +++ b/hotspot/src/share/vm/utilities/hashtable.hpp Mon Jun 25 21:33:35 2012 -0400 @@ -217,12 +217,7 @@ } // Free the buckets in this hashtable - void free_buckets() { - if (NULL != _buckets) { - FREE_C_HEAP_ARRAY(HashtableBucket, _buckets); - _buckets = NULL; - } - } + void free_buckets(); public: int table_size() { return _table_size; }