< prev index next >

src/hotspot/share/classfile/javaClasses.cpp

Print this page
rev 54472 : 8221836: Avoid recalculating String.hash when zero
Reviewed-by: jrose, adinn
Contributed-by: peter.levart@gmail.com, claes.redestad@oracle.com

@@ -158,10 +158,11 @@
   compute_offset(dest_offset, ik, name, signature_symbol, is_static);
 }
 
 int java_lang_String::value_offset  = 0;
 int java_lang_String::hash_offset   = 0;
+int java_lang_String::hashIsZero_offset = 0;
 int java_lang_String::coder_offset  = 0;
 
 bool java_lang_String::initialized  = false;
 
 bool java_lang_String::is_instance(oop obj) {

@@ -177,11 +178,12 @@
   compute_offset(offset, klass, name, vmSymbols::signature(), is_static)
 
 #define STRING_FIELDS_DO(macro) \
   macro(value_offset, k, vmSymbols::value_name(), byte_array_signature, false); \
   macro(hash_offset,  k, "hash",                  int_signature,        false); \
-  macro(coder_offset, k, "coder",                 byte_signature,       false)
+  macro(hashIsZero_offset, k, "hashIsZero",       bool_signature,       false); \
+  macro(coder_offset, k, "coder",                 byte_signature,       false);
 
 void java_lang_String::compute_offsets() {
   if (initialized) {
     return;
   }

@@ -505,22 +507,42 @@
   }
   return result;
 }
 
 unsigned int java_lang_String::hash_code(oop java_string) {
+  // The hash and hashIsZero fields are subject to a benign data race,
+  // making it crucial to ensure that any observable result of the
+  // calculation in this method stays correct under any possible read of
+  // these fields. Necessary restrictions to allow this to be correct
+  // without explicit memory fences or similar concurrency primitives is
+  // that we can ever only write to one of these two fields for a given
+  // String instance, and that the computation is idempotent and derived
+  // from immutable state
+  assert(initialized && (hash_offset > 0) && (hashIsZero_offset > 0), "Must be initialized");
+  if (java_lang_String::hash_is_set(java_string)) {
+    return java_string->int_field(hash_offset);
+  }
+
   typeArrayOop value  = java_lang_String::value(java_string);
   int          length = java_lang_String::length(java_string, value);
-  // Zero length string will hash to zero with String.hashCode() function.
-  if (length == 0) return 0;
-
   bool      is_latin1 = java_lang_String::is_latin1(java_string);
 
+  unsigned int hash = 0;
+  if (length > 0) {
   if (is_latin1) {
-    return java_lang_String::hash_code(value->byte_at_addr(0), length);
+      hash = java_lang_String::hash_code(value->byte_at_addr(0), length);
+    } else {
+      hash = java_lang_String::hash_code(value->char_at_addr(0), length);
+    }
+  }
+
+  if (hash != 0) {
+    java_string->int_field_put(hash_offset, hash);
   } else {
-    return java_lang_String::hash_code(value->char_at_addr(0), length);
+    java_string->bool_field_put(hashIsZero_offset, true);
   }
+  return hash;
 }
 
 char* java_lang_String::as_quoted_ascii(oop java_string) {
   typeArrayOop value  = java_lang_String::value(java_string);
   int          length = java_lang_String::length(java_string, value);
< prev index next >