开发者

System.identityHashCode和hashCode的区别及说明

目录
  • System.identityHashCode和hashCode的区别
    • System.identityHashCode底层实现
    • Object.hashCode底层实现
  • 总结

    System.identityHashCode和hashCode的区别

    测试:

    public class HashCodeDemo {
    
        public static void main(String[] args) {
            String str = new String("test");
            String str2= new String("test");
    
            System.out.println(str.hashCode());
            System.out.println(str2.hashCode());
    
            System.out.println(System.identityHashCode(str));
            System.out.println(System.identityHashCode(str2));
    
            System.out.println("----------------test------------");
    
            Test test = new Test();
            Test test2 = new Test();
    
            System.out.println(test.hashCode());
            System.out.println(test2.hashCode());
    
            System.out.println(System.identityHashCode(test));
            System.out.println(System.identityHashCode(test2));
        }
    }
    

    运行结果:

    System.identityHashCode和hashCode的区别及说明

    为什么String调用hashCode()和System.identityHashCode()返回值不同呢?为什么Test调用hashCode()和System.identityHashCode()返回值又相同呢?

    System.identityHashCode()和hashCode()到底有什么不同呢?这里个人简单分析一下,有不足之处劳烦指出。

    System.identityHashCode底层实现

    System.identityHashCode底层调用C语言System.c来实现

    openjdk源码路径:jdk-935758609767\src\share\native\Java\lang\System.c

    // 核心代码:
    Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x)
    {
        return JVM_IHashCode(env, x);
    }
    

    其中调用jvm.cpp的JVM_IHashCode()方法

    hotspot源码路径:hotspot-37240c1019fd\src\share\vm\prims\jvm.cpp

    JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
      JVMWrapper("JVM_IHashCode");
      // as implemented in the classic virtual MAChine; return 0 if object is NULL
      return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
    JVM_END
    

    再来看一下synchronizer.cpp的ObjectSynchronizer::FastHashCode()方法

    hotspot源码路径:hotspot-37240c1019fd\src\share\vm\runtime\synchronizer.cpp

    intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
      if (UseBiasedLocking) {
        // NOTE: many places throughout the JVM do not expect a safepoint
        // to be taken here, in particular most operations on perm gen
        // objects. However, we only ever bias Java instances and all of
        // the call sites of identity_hash that might revoke biases have
        // been checked to make sure they can handle a safepoint. The
        // added check of the bias pattern is to avoid useless calls to
        // thread-local storage.
        if (obj->mark()->javascript;has_bias_pattern()) {
          // Box and unbox the raw reference just in case we cause a STW safepoint.
          Handle hobj (Self, obj) ;
          // Relaxing assertion for bug 6320749.
          assert (Universe::verify_in_progress() ||
                  !SafepointSynchronize::is_at_safepoint(),
                 "biases should not be seen by VM thread here");
          BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());
          obj = hobj() ;
          assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
        }
      }
    
      // hashCode() is a heap mutator ...
      // Relaxing assertion for bug 6320749.
      assert (Universe::verify_in_progress() ||
              !SjavascriptafepointSynchronize::is_at_safepoint(), "invariant") ;
      assert (Universe::verify_in_progress() ||
              Self->is_Java_thread() , "invariant") ;
      assert (Universe::verify_in_progress() ||
             ((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant") ;
    
      ObjectMonitor* monitor = NULL;
      markOop temp, test;
      intptr_t hash;
      markOop mark = ReadStableMark (obj);
    
      // object should remain ineligible for biased locking
      assert (!mark->has_bias_pattern(), "invariant") ;
    
      if (mark->is_neutral()) {
        hash = mark->hash();              // this is a normal header
        if (hash) {                       // if it has hash, just return it
          return hash;
        }
        hash = get_next_hash(Self, obj);  // allocate a new hash code
        temp = mark->copy_set_hash(hash); // merge the hash code into header
        // use (machine word version) atomic operation to install the hash
        test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);
        if (test == mark) {
          return hash;
        }
        // If atomic operation failed, we must inflate the header
        // into heavy weight monitor. We could add more code here
        // for fast path, but it does not worth the complexity.
      } else if (mark->has_monitor())http://www.devze.com {
        monitor = mark->monitor();
        temp = monitor->header();
        assert (temp->is_neutral(), "invariant") ;
        hash = temp->hash();
        if (hash) {
          return hash;
        }
        // Skip to the following code to reduce code size
      } else if (Self->is_lock_owned((address)mark->locker())) {
        temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned
        assert (temp->is_neutral(), "invariant") ;
        hash = temp->hash();              // by current thread, check if the displaced
        if (hash) {                       // header contains hash code
          return hash;
        }
        // WARNING:
        //   The displaced header is strictly immutable.
        // It can NOT be changed in ANY cases. So we have
        // to inflate the header into heavyweight monitor
        // even the current thread owns the lock. The reason
        // is the BasicLock (stack slot) will be asynchronously
        // read by other threads during the inflate() function.
        // Any change to stack may not propagate to other threads
        // correctly.
      }
    
      // Inflate the monitor to set hash code
      monitor = ObjectSynchronizer::inflate(Self, obj);
      // Load displaced header and check it has hash code
      mark = monitor->header();
      assert (mark->is_neutral(), "invariant") ;
      hash = mark->hash();
      if (hash == 0) {
        hash = get_next_hash(Self, obj);
        temp = mark->copy_set_hash(hash); // merge hash code into header
        assert (temp->is_neutral(), "invariant") ;
        test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark);
        if (test != mark) {
          // The only update to the header in the monitor (outside GC)
          // is install the hash code. If someone add new usage of
          // displaced header, please update this code
          hash = test->hash();
          assert (test->is_neutral(), "invariant") ;
          assert (hash != 0, "Trivial unexpected object/monitor header usage.");
        }
      }
      // We finally get the hash
      return hash;
    }
    

    其中,调用的核心方法是get_next_hash()

    static inline intptr_t get_next_hash(Thread * Self, oop obj) {
      intptr_t value = 0 ;
      if (hashCode == 0) {
         // This form uses an unguarded global Park-Miller RNG,
         // so it's possible for two threads to race and generate the same RNG.
         // On MP system we'll have lots of RW Access to a global, so the
         // mechanism induces lots of coherency traffic.
         value = os::random() ;
      } else
      if (hashCode == 1) {
         // This variation has the property of being stable (idempotent)
         // between STW operations.  This can be useful in some of the 1-0
         // synchronization schemes.
         intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
         value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
      } else
      if (hashCode == 2) {
         value = 1 ;            // for sensitivity testing
      } else
      if (hashCode == 3) {
         value = ++GVars.hcSequence ;
      } else
      if (hashCode == 4) {
         value = cast_from_oop<intptr_t>(obj) ;
      } else {
         // Marsaglia's xor-shift scheme with thread-specific state
         // This is probably the best overall implementation -- we'll
         // likely make this the default in future releases.
         unsigned t = Self->_hashStateX ;
         t ^= (t << 11) ;
         Self->_hashStateX = Self->_hashStateY ;
         Self->_hashStateY = Self->_hashStateZ ;
         Self->_hashStateZ = Self->_hashStateW ;
         unsigned v = Self->_hashStateW ;
         v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
         Self->_hashStawww.devze.comteW = v ;
         value = v ;
      }
    
      value &= markOopDesc::hash_mask;
      if (value == 0) value = 0xBAD ;
      assert (value != markOopDesc::no_hash, "invariant") ;
      TEVENT (hashCode: GENERATE) ;
      return value;
    }
    

    根据hashcode值,可以分为以下方法:

    0- 随机生成值

    1- 获取对象的实际内存地址

    2- 返回1,用于灵敏度测试

    3- 自增 4- 第二种的变种

    5- xorshift算法,有兴javascript趣可以看一下https://en.wikipedia.org/wiki/Xorshift

    jdk1.8前,默认hashcode为0,可通过globals.hpp文件查看,调用第一个方法,随机生成hashcode

    globals.hpp源码路径:hotspot\src\share\vm\runtime\globals.hpp

    product(intx, hashCode, 0,“(Unstable) select hashCode generation algorithm”)

    jdk1.8后,默认为5,使用xorshift 算法生成hashcode

    product(intx, hashCode, 5,“(Unstable) select hashCode generation algorithm”)

    同时可以通过-XX:hashCode=N来修改jvm默认值来修改调用方法

    查看jvm默认值java -XX:+PrintFlagsFinal -version

    System.identityHashCode和hashCode的区别及说明

    Object.hashCode底层实现

    System.identityHashCode和hashCode的区别及说明

    通过查看openjdk源码Object.c

    Object.c源码路径:jdk-935758609767\src\share\native\java\lang\Object.c

    static JNINativeMethod methods[] = {
        {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
        {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
        {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
        {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
        {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
    };
    

    发现还是和System.identityHashCode一样都是调用JVM_IHashCode方法。

    总结

    • 当hashCode()未被重写时,System.identityHashCode()和hashCode()返回值相同,都是调用底层JVM_IHashCode方法
    • 当hashCode()被重写,则System.identityHashCode()和hashCode()返回值不同。hashCode()返回重写结果,System.identityHashCode()返回底层生成hashcode

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜