您的位置:首页 > 移动开发 > Objective-C

Java的Object.hashCode()的返回值到底是不是对象内存地址?

2016-12-25 00:57 579 查看
刚学Java的时候我也有过这种怀疑,但一直没有验证;最近在OSCHINA上看到有人在回答问题时也这么说,于是萌生了一探究竟的想法——java.lang.Object.hashCode()的返回值到底是不是对象内存地址?

(顺带回顾一下JNI)


hashCode契约

说到这个问题,大家的第一反应一定和我一样——去查Object.hashCode的源码,但翻开源码,看到的却是这样的(Oracle JDK 8):

view
sourceprint?

01.
<code
class
=
"language-java
hljs "
>
/**


02.
*
Returns a hash code value for the object. This method is


03.
*
supported for the benefit of hash tables such as those provided by


04.
*
{@link java.util.HashMap}.


05.
*
<p>


06.
*
The general contract of {@code hashCode}is:


07.
*
<ul>


08.
*
<li>Whenever it is invoked on the same object more than once during


09.
*
an execution of a Java application,the {@code hashCode}method


10.
*
must consistently return the same integer,provided no information


11.
*
used in {@code equals}comparisons on the object is modified.


12.
*
This integer need not remain consistent from one execution of an


13.
*
application to another execution of the same application.


14.
*
<li>If two objects are equal according to the {@code equals(Object)}


15.
*
method,then calling the {@code hashCode}method on each of


16.
*
the two objects must produce the same integer result.


17.
*
<li>It is <em>not</em> required that if two objects are unequal


18.
*
according to the {@link java.lang.Object#equals(java.lang.Object)}


19.
*
method,then calling the {@code hashCode}method on each of the


20.
*
two objects must produce distinct integer results.  However,the


21.
*
programmer should be aware that producing distinct integer results


22.
*
for unequal objects may improve the performance of hash tables.


23.
*
</ul>


24.
*
<p>


25.
*
As much as is reasonably practical,the hashCode method defined by


26.
*
class {@code Object}does return distinct integers for distinct


27.
*
objects. (This is typically implemented by converting the internal


28.
*
address of the object into an integer,but this implementation


29.
*
technique is not required by the


30.
*
Java™ programming language.)


31.
*


32.
*
@return  a hash code value for this object.


33.
*
@see     java.lang.Object#equals(java.lang.Object)


34.
*
@see     java.lang.System#identityHashCode


35.
*/


36.
public
native
int
hashCode();</code>




Object.hashCode是一个native方法,看不到源码(Java代码,Oracle的JDK是看不到的,OpenJDK或其他开源JRE是可以找到对应的C/C++代码)。

上面这段注释指出了Object.hashCode()在JRE(Java Runtime Library)中应该遵循的一些契约(contract):

(PS:所谓契约当然是大家一致达成的,各个JVM厂商都会遵循)

一致性(consistent),在程序的一次执行过程中,对同一个对象必须一致地返回同一个整数。

如果两个对象通过equals(Object)比较,结果相等,那么对这两个对象分别调用hashCode方法应该产生相同的整数结果。(PS:这里equals和hashCode说的都是Object类的)

如果两个对象通过java.lang.Object.equals(java.lang.Ojbect)比较,结果不相等,不必保证对这两个对象分别调用hashCode也返回两个不相同的整数。

实际上java.lang包里面的类,都是JRE必须的,属于运行时库(Runtime Library),这也是为什么很多JRE下该类的class文件被打包到rt.jar中的原因(应该是Runtime的简写)。

而这些运行时库一般都是跟JDK/JRE一起发布的;所以,对于不同的JRE环境,问题的答案未必相同。

考虑到具体JVM厂商实现的Object.hashCode相对较复杂,下面先通过另一思路对开头提出的问题进行探究。

最后我们再找一些开源JRE的Object.hashCode的具体实现作简要分析。


Java中如何获得对象内存地址?

看不到Object.hashCode的源码,反过来,我们可以得到对象的内存地址和Object.hashCode比较,也能得出结论。

要验证这个问题自然需要一种得到对象内存地址的方法,但Java本身并没有提供类似的方法;这也是我在初学Java时没有验证这个问题的原因。

“内存地址”在Java里得不到,但在C/C++中却很容易得到。于是,我们想到——通过JNI让Java代码调用一段C/C++代码来得到对象内存地址。

这里可能需要考虑的还有一点——用Java什么类型能放得下C/C++的指针?

在64位机器上,C/C++的指针是8字节;32位是4字节。

嗯(⊙_⊙)~ 不管怎样Java的long都是8字节,足矣~


Think in Java——接口和测试

假设我们已经有了一个NativeUtils.java:

view
sourceprint?

1.
<code
class
=
"language-java
hljs "
>
class
NativeUtils
{


2.
public
native
static
long
getNativePointer(Object
o);


3.
}</code>




并且已经实现了getNativePointer方法。

那么验证开头提出的问题就变得异常简单了:

view
sourceprint?

01.
<code
class
=
"language-java
hljs "
>
class
TestNativeUtils
{


02.
public
static
void
main(String
args[]) {


03.
Object
o =
new
Object();


04.


05.
long
nptr
= NativeUtils.getNativePointer(o);


06.
long
hash
= o.hashCode();


07.


08.
System.out.println(String.format(
"hash:
%x,nptr: %x"
,
hash,nptr));


09.
}


10.
}</code>




Think in C++——实现native方法

好了说干就干,现在就差那个native的getNativePointer了。

用javah生成对应的.h文件:

$ javah NativeUtils

该命令执行后生成了NativeUtils.h:

view
sourceprint?

01.
<code
class
=
"language-c
hljs "
>
/*
DO NOT EDIT THIS FILE - it is machine generated */


02.
#include
<jni.h>


03.
/*
Header for class NativeUtils */


04.


05.
#ifndef
_Included_NativeUtils


06.
#define
_Included_NativeUtils


07.
#ifdef
__cplusplus


08.
extern
"C"
{


09.
#endif


10.
/*


11.
*
Class:     NativeUtils


12.
*
Method:    getNativePointer


13.
*
Signature: (Ljava/lang/Object;)J


14.
*/


15.
JNIEXPORT
jlong JNICALL Java_NativeUtils_getNativePointer


16.
(JNIEnv
*,jclass,jobject);


17.


18.
#ifdef
__cplusplus


19.
}


20.
#endif


21.
#endif</code>




接着实现这个Java_NativeUtils_getNativePointer,文件命名为NativeUtils.cc:

view
sourceprint?

1.
<code
class
=
"language-c
hljs "
>#include
"NativeUtils.h"


2.


3.
JNIEXPORT
jlong JNICALL


4.
Java_NativeUtils_getNativePointer(JNIEnv
*env,jclass clazz,jobject o)


5.
{


6.
return
reinterpret_cast<jlong>(o);


7.
}</code>




编译为动态库:

$ g++-shared -o libnative-utils.so NativeUtils.cc

可能因为找不到jni.h,报错:

NativeUtils.h:2:17: fatal error: jni.h: No such file or directory

在JDK安装目录下查找jni.h:

user@host:/usr/lib/jvm/java-7-openjdk-amd64$ find . -name jni.h

./include/jni.h

知道jni.h路径后,用-I选项加到编译命令上再次编译:

$ g++-shared -I/usr/lib/jvm/java-7-openjdk-amd64/include/ -o libnative-utils.so NativeUtils.cc

OK, 编译成功,生成了 libnative-utils.so 文件。


Run in shell——在shell环境运行

下面就让TestNativeUtils在shell环境执行起来。

首先,编译NativeUtils和TestNativeUtils:

$ javac NativeUtils.java

$ javac TestNativeUtils.java

分别生成了NativeUtils.class和TestNativeUtils.class

好了,就差临门一脚了——执行class文件:

$ java TestNativeUtils

居然出错了:

view
sourceprint?

1.
<code
class
=
"
hljs avrasm"
>Exception
in thread
"main"
java.lang.UnsatisfiedLinkError:
NativeUtils.getNativePointer(Ljava/lang/Object;)J


2.
at
NativeUtils.getNativePointer(Native Method)


3.
at
TestNativeUtils.main(TestNativeUtils.java:
5
)</code>




加载动态库到我们的程序中

到目前为止,我们的Java代码并没有实现NativeUtils.getNativePointer;所以,会有上面的错误。

必须在调用NativeUtils.getNativePointer前,将我们编译好的动态库加载上。可以用static代码块,以保证在调用前完成加载;修改后的NativeUtils:

view
sourceprint?

1.
<code
class
=
"language-java
hljs "
>
class
NativeUtils
{


2.
static
{


3.
System.loadLibrary(
"native-utils"
);


4.
}


5.


6.
public
native
static
long
getNativePointer(Object
o);


7.
}</code>




让JVM能找到动态库

再次编译、执行:

$ javac NativeUtils.java

$ java TestNativeUtils

又有错误,但已经和刚才不一样了:

view
sourceprint?

1.
<code
class
=
"
hljs avrasm"
>Exception
in thread
"main"
java.lang.UnsatisfiedLinkError:
no
native
-utils
in java.library.path


2.
at
java.lang.ClassLoader.loadLibrary(ClassLoader.java:
1886
)


3.
at
java.lang.Runtime.loadLibrary0(Runtime.java:
849
)


4.
at
java.lang.System.loadLibrary(System.java:
1088
)


5.
at
NativeUtils.<clinit>(NativeUtils.java:
3
)


6.
at
TestNativeUtils.main(TestNativeUtils.java:
5
)</code>




这次错误是:没有在java.library.path中找到native-utils,可以在javac命令-D参数上将当期目录加到java.library.path上,让其能够找到native-utils:

$ java -Djava.library.path=. TestNativeUtils

hash: 4f5f1ace,nptr: 7f223a5fb958


All in one —— Makefile

上面的多个命令可以写到一个Makefile里,可以实现“一键执行”:

view
sourceprint?

01.
<code
class
=
"language-Makefile
hljs http"
>test:
runtest


02.


03.
all:
libnative-utils.so


04.


05.
JNI_INCLUDE=/usr/lib/jvm/java-
7
-openjdk-amd64/include


06.


07.
NativeUtils.
class
:
NativeUtils.java


08.
javac
NativeUtils.java


09.


10.
TestNativeUtils.
class
:
TestNativeUtils.java


11.
javac
TestNativeUtils.java


12.


13.
NativeUtils.h:
NativeUtils.java


14.
javah
-jni NativeUtils


15.


16.
libnative-utils.so:
NativeUtils.cc NativeUtils.h


17.
g++
-shared -I${JNI_INCLUDE}-o libnative-utils.so NativeUtils.cc


18.


19.
runtest:
TestNativeUtils.
class
libnative-utils.so


20.
@echo
"run
test:"


21.
java
-Djava.library.path=. TestNativeUtils


22.


23.
clean:


24.
rm
-v *.
class
*.so
*.h</code>




几个JRE的具体实现

说道开源的Java运行环境,首先能想到的自然是OpenJDK和Android,下面分别简要分析。


hashCode on Android

Android的Object.hashCode和Oracle JDK的略有不同:

view
sourceprint?

01.
<code
class
=
"language-java
hljs "
>
private
transient
int
shadow$_monitor_;


02.


03.
public
int
hashCode()
{


04.
int
lockWord
= shadow$_monitor_;


05.
final
int
lockWordMask
=
0xC0000000
;
//
Top 2 bits.


06.
final
int
lockWordStateHash
=
0x80000000
;
//
Top 2 bits are value 2 (kStateHash).


07.
if
((lockWord
& lockWordMask) == lockWordStateHash) {


08.
return
lockWord
& ~lockWordMask;
//


09.
}


10.
return
System.identityHashCode(
this
);


11.
}</code>




(PS:估计shadow$_monitor_应该是hash值的一个cache,第一次需要计算一下,以后都不用计算了)

第一次执行时,shadow$_monitor_的值为0,将会调用System.identityHashCode:

view
sourceprint?

1.
<code
class
=
"language-java
hljs "
>
public
static
native
int
identityHashCode(Object
anObject);</code>




而它是一个native的方法,对应代码(java_lang_System.cc):

view
sourceprint?

1.
<code
class
=
"language-c
hljs "
>
static
jint
System_identityHashCode(JNIEnv* env,jclass,jobject javaObject) {


2.
if
(UNLIKELY(javaObject
== nullptr)) {


3.
return
0
;


4.
}


5.
ScopedFastNativeObjectAccess
soa(env);


6.
mirror::Object*
o = soa.Decode<mirror::Object*>(javaObject);


7.
return
static_cast<jint>(o->IdentityHashCode());


8.
}</code>




很显然,这里的关键在于mirror::Object::IdentityHashCode:

view
sourceprint?

01.
<code
class
=
"
hljs cpp"
>int32_t
Object::IdentityHashCode()
const
{


02.
mirror::Object*
current_this = const_cast<mirror::Object*>(
this
);


03.
while
(
true
)
{


04.
LockWord
lw = current_this->GetLockWord(
false
);


05.
switch
(lw.GetState())
{


06.
case
LockWord::kUnlocked:
{
//
kUnlocked 是 LockWord的默认State值


07.
//
Try to compare and swap in a new hash,if we succeed we will return the hash on the next


08.
//
loop iteration.


09.
LockWord
hash_<a href=
"http://www.it165.net/edu/ebg/"
target=
"_blank"
class
=
"keylink"
>word</a>(LockWord::FromHashCode(GenerateIdentityHashCode()));


10.
DCHECK_EQ(hash_<a
href=
"http://www.it165.net/edu/ebg/"
target=
"_blank"
class
=
"keylink"
>word</a>.GetState(),
LockWord::kHashCode);


11.
if
(const_cast<Object*>(
this
)->CasLockWordWeakRelaxed(lw,
hash_word)) {


12.
return
hash_word.GetHashCode();


13.
}


14.
break
;


15.
}


16.
case
LockWord::kThinLocked:
{


17.
//
Inflate the thin lock to a monitor and stick the hash code inside of the monitor. May


18.
//
fail spuriously.


19.
Thread*
self = Thread::Current();


20.
StackHandleScope<
1
>
hs(self);


21.
Handle<mirror::Object>
h_this(hs.NewHandle(current_this));


22.
Monitor::InflateThinLocked(self,
h_this,lw,GenerateIdentityHashCode());


23.
//
A GC may have occurred when we switched to kBlocked.


24.
current_this
= h_this.Get();


25.
break
;


26.
}


27.
case
LockWord::kFatLocked:
{


28.
//
Already inflated,return the has stored in the monitor.


29.
Monitor*
monitor = lw.FatLockMonitor();


30.
DCHECK(monitor
!= nullptr);


31.
return
monitor->GetHashCode();


32.
}


33.
case
LockWord::kHashCode:
{
//
以后调用


34.
return
lw.GetHashCode();


35.
}


36.
default
:
{


37.
LOG(FATAL)
<<
"Invalid
state during hashcode "
<<
lw.GetState();


38.
break
;


39.
}


40.
}


41.
}


42.
LOG(FATAL)
<<
"Unreachable"
;


43.
return
0
;


44.
}</code>




这段代码可以看出——ART的Object.hashCode确实是有cache的,对于同一个Ojbect,第一次调用Object.hashCode将会执行实际的计算并记入cache,以后直接从cache中取出。

真正计算hashcode的是GenerateIdentityHashCode:

view
sourceprint?

01.
<code
class
=
"
hljs cpp"
>int32_t
Object::GenerateIdentityHashCode() {


02.
static
AtomicInteger
seed(
987654321
+
std::time(nullptr));


03.
int32_t
expected_value,new_value;


04.
do
{


05.
expected_value
= static_cast<uint32_t>(seed.LoadRelaxed());


06.
new_value
= expected_value *
1103515245
+
12345
;


07.
}
while
((expected_value
& LockWord::kHashMask) ==
0
||


08.
!seed.CompareExchangeWeakRelaxed(expected_value,
new_value));


09.
return
expected_value
& LockWord::kHashMask;


10.
}</code>




从GenerateIdentityHashCode可以看出,ART的Object.hashCode的返回值和对象的地址并没有直接的关系。


hashCode on OpenJDK

OpenJDK项目首页:openjdk.java.net

TODO: OpenJDK上的hashCode具体实现和简要分析

Object.c

view
sourceprint?

01.
<code
class
=
"
hljs coffeescript"
>
static
JNINativeMethod
methods[] = {


02.
{
"hashCode"
,
"()I"
,
(
void
*)&JVM_IHashCode},


03.
{
"wait"
,
"(J)V"
,
(
void
*)&JVM_MonitorWait},


04.
{
"notify"
, 
"()V"
,
(
void
*)&JVM_MonitorNotify},


05.
{
"notifyAll"
,
"()V"
,
(
void
*)&JVM_MonitorNotifyAll},


06.
{
"clone"
,
"()Ljava/lang/Object;"
,
(
void
*)&JVM_Clone},


07.
};


08.


09.
JNIEXPORT
void
JNICALL


10.
Java_java_lang_Object_registerNatives(JNIEnv
*env,jclass cls)


11.
{


12.
(*env)->RegisterNatives(env,
cls,


13.
methods,
sizeof(methods)/sizeof(methods[
0
]));


14.
}


15.


16.
JNIEXPORT
jclass JNICALL


17.
Java_java_lang_Object_getClass(JNIEnv
*env,jobject
this
)


18.
{


19.
if
(
this
==
NULL) {


20.
JNU_ThrowNullPointerException(env,
NULL);


21.
return
0
;


22.
}
else
{


23.
return
(*env)->GetObjectClass(env,
this
);


24.
}


25.
}</code>




这段代码指出了Object.hashCode对应的C函数为JVM_IHashCode,下面需要找到JVM_IHashCode的代码jvm.cpp:

view
sourceprint?

1.
<code
class
=
"
hljs lasso"
>JVM_ENTRY(jint,
JVM_IHashCode(JNIEnv* env,jobject handle))


2.
JVMWrapper(
"JVM_IHashCode"
);


3.
//
as implemented in the classic virtual machine; return 0 if object is NULL


4.
return
handle
== NULL ?
0
:
ObjectSynchronizer::FastHashCode (THREAD,JNIHandles::resolve_non_null(handle)) ;


5.
JVM_END</code>




这里只是一个包装,实际计算hashCode的是ObjectSynchronizer::FastHashCode,位于synchronizer.cpp:

view
sourceprint?

001.
<code
class
=
"
hljs lasso"
>intptr_t
ObjectSynchronizer::FastHashCode (Thread * Self,oop obj) {


002.
if
(UseBiasedLocking)
{


003.
//
NOTE: many places throughout the JVM do not expect a safepoint


004.
//
to be taken here,in particular most operations on perm gen


005.
//
objects. However,we only ever bias Java instances and all of


006.
//
the call sites of identity_hash that might revoke biases have


007.
//
been checked to make sure they can handle a safepoint. The


008.
//
added check of the bias pattern is to avoid useless calls to


009.
//
thread-local storage.


010.
if
(obj->mark()->has_bias_pattern())
{


011.
//
Box and unbox the raw reference just in case we cause a STW safepoint.


012.
Handle
hobj (Self,obj) ;


013.
//
Relaxing assertion for bug 6320749.


014.
assert
(Universe::verify_in_progress()
||


015.
!SafepointSynchronize::is_at_safepoint(),


016.
"biases
should not be seen by VM thread here"
);


017.
BiasedLocking::revoke_and_rebias(hobj,
false
,
JavaThread::current());


018.
obj
= hobj() ;


019.
assert
(!obj->mark()->has_bias_pattern(),
"biases
should be revoked by now"
);


020.
}


021.
}


022.


023.
//
hashCode() is a heap mutator ...


024.
//
Relaxing assertion for bug 6320749.


025.
assert
(Universe::verify_in_progress()
||


026.
!SafepointSynchronize::is_at_safepoint(),
"invariant"
)
;


027.
assert
(Universe::verify_in_progress()
||


028.
Self->is_Java_thread()
,
"invariant"
)
;


029.
assert
(Universe::verify_in_progress()
||


030.
((JavaThread
*)Self)->thread_state() != _thread_blocked,
"invariant"
)
;


031.


032.
ObjectMonitor*
monitor = NULL;


033.
markOop
temp,test;


034.
intptr_t
hash;


035.
markOop
mark = ReadStableMark (obj);


036.


037.
//
object should remain ineligible for biased locking


038.
assert
(!mark->has_bias_pattern(),
"invariant"
)
;


039.


040.
if
(mark->is_neutral())
{


041.
hash
= mark->hash();         
//
this is a normal header


042.
if
(hash)
{                       
//
if it has hash,just return it


043.
return
hash;


044.
}


045.
hash
= get_next_hash(Self,obj);
//
allocate a new hash code


046.
temp
= mark->copy_set_hash(hash);
//
merge the hash code into header


047.
//
use (machine word version) atomic operation to install the hash


048.
test
= (markOop) Atomic::cmpxchg_ptr(temp,obj->mark_addr(),mark);


049.
if
(test
== mark) {


050.
return
hash;


051.
}


052.
//
If atomic operation failed,we must inflate the header


053.
//
into heavy weight monitor. We could add more code here


054.
//
for fast path,but it does not worth the complexity.


055.
}
else
if
(mark->has_monitor())
{


056.
monitor
= mark->monitor();


057.
temp
= monitor->header();


058.
assert
(temp->is_neutral(),
"invariant"
)
;


059.
hash
= temp->hash();


060.
if
(hash)
{


061.
return
hash;


062.
}


063.
//
Skip to the following code to reduce code size


064.
}
else
if
(Self->is_lock_owned((address)mark->locker()))
{


065.
temp
= mark->displaced_mark_helper();
//
this is a lightweight monitor owned


066.
assert
(temp->is_neutral(),
"invariant"
)
;


067.
hash
= temp->hash();          
//
by current thread,check if the displaced


068.
if
(hash)
{                       
//
header contains hash code


069.
return
hash;


070.
}


071.
//
WARNING:


072.
//
The displaced header is strictly immutable.


073.
//
It can NOT be changed in ANY cases. So we have


074.
//
to inflate the header into heavyweight monitor


075.
//
even the current thread owns the lock. The reason


076.
//
is the BasicLock (stack slot) will be asynchronously


077.
//
read by other threads during the inflate() function.


078.
//
Any change to stack may not propagate to other threads


079.
//
correctly.


080.
}


081.


082.
//
Inflate the monitor to set hash code


083.
monitor
= ObjectSynchronizer::inflate(Self,obj);


084.
//
Load displaced header and check it has hash code


085.
mark
= monitor->header();


086.
assert
(mark->is_neutral(),
"invariant"
)
;


087.
hash
= mark->hash();
//
取出缓存


088.
if
(hash
==
0
)
{


089.
hash
= get_next_hash(Self,obj);
//
实际计算


090.
temp
= mark->copy_set_hash(hash);
//
merge hash code into header


091.
assert
(temp->is_neutral(),
"invariant"
)
;


092.
test
= (markOop) Atomic::cmpxchg_ptr(temp,monitor,mark);


093.
if
(test
!= mark) {


094.
//
The only update to the header in the monitor (outside GC)


095.
//
is install the hash code. If someone add new usage of


096.
//
displaced header,please update this code


097.
hash
= test->hash();


098.
assert
(test->is_neutral(),
"invariant"
)
;


099.
assert
(hash
!=
0
,
"Trivial
unexpected object/monitor header usage."
);


100.
}


101.
}


102.
//
We finally get the hash


103.
return
hash;


104.
}</code>




又是假牙,实际计算hashCode的是get_next_hash,代码和ObjectSynchronizer::FastHashCode相邻:

view
sourceprint?

01.
<code
class
=
"
hljs lasso"
>
//
hashCode() generation :


02.
//


03.
//
Possibilities:


04.
//
* MD5Digest of {obj,stwRandom}


05.
//
* CRC32 of {obj,stwRandom}or any linear-feedback shift register function.


06.
//
* A DES- or AES-style SBox[] mechanism


07.
//
* One of the Phi-based schemes,such as:


08.
//
2654435761 = 2^32 * Phi (golden ratio)


09.
//
HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;


10.
//
* A variation of Marsaglia's shift-xor RNG scheme.


11.
//
* (obj ^ stwRandom) is appealing,but can result


12.
//
in undesirable regularity in the hashCode values of adjacent objects


13.
//
(objects allocated back-to-back,in particular). This could potentially


14.
//
result in hashtable collisions and reduced hashtable efficiency.


15.
//
There are simple ways to "diffuse" the middle address bits over the


16.
//
generated hashCode values:


17.
//


18.


19.
static
inline
intptr_t get_next_hash(Thread * Self,oop obj) {


20.
intptr_t
value =
0
;


21.
if
(hashCode
==
0
)
{


22.
//
This form uses an unguarded global Park-Miller RNG,


23.
//
so it's possible for two threads to race and generate the same RNG.


24.
//
On MP system we'll have lots of RW access to a global,so the


25.
//
mechanism induces lots of coherency traffic.


26.
value
= os::random() ;
//
随机数


27.
}
else


28.
if
(hashCode
==
1
)
{


29.
//
This variation has the property of being stable (idempotent)


30.
//
between STW operations.  This can be useful in some of the 1-0


31.
//
synchronization schemes.


32.
//
地址基础上hack


33.
intptr_t
addrBits = intptr_t(obj) >>
3
;


34.
value
= addrBits ^ (addrBits >>
5
)
^ GVars.stwRandom ;


35.
}
else


36.
if
(hashCode
==
2
)
{


37.
value
=
1
;        
//
for sensitivity testing, 实际不会使用


38.
}
else


39.
if
(hashCode
==
3
)
{


40.
value
=++GVars.hcSequence ;


41.
}
else


42.
if
(hashCode
==
4
)
{


43.
value
=intptr_t(obj) ; 
//
直接用地址


44.
}
else
{


45.
//
Marsaglia's xor-shift scheme with thread-specific state


46.
//
This is probably the best overall implementation -- we'll


47.
//
likely make this the default in future releases.


48.
unsigned
t = Self->_hashStateX ;


49.
t
^= (t <<
11
)
;


50.
Self->_hashStateX
= Self->_hashStateY ;


51.
Self->_hashStateY
= Self->_hashStateZ ;


52.
Self->_hashStateZ
= Self->_hashStateW ;


53.
unsigned
v = Self->_hashStateW ;


54.
v
= (v ^ (v >>
19
))
^ (t ^ (t >>
8
))
;


55.
Self->_hashStateW
= v ;


56.
value
=v ;


57.
}


58.


59.
value
&= markOopDesc::hash_mask;


60.
if
(value
==
0
)
value =
0xBAD
;


61.
assert
(value
!= markOopDesc::no_hash,
"invariant"
)
;


62.
TEVENT
(hashCode: GENERATE) ;


63.
return
value;


64.
}</code>




这段代码可以看出OpenJDK一共实现了5中不同的计算hash值的方法,通过

这段代码中hashCode进行切换。其中hashCode == 4的是直接使用地址的(前面的实验说明OpenJDK默认情况下并没有使用这种方式,或许可以通过运行/编译时参数进行选择)。

结论

前面通过JNI验证已经能够得到很显然的结论,hashCode返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。

我的运行环境:

OS:

Ubuntu 12.04 64bit Desktop |

JDK:

java version “1.7.0_55”

OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1ubuntu1~0.12.04.2) \

OpenJDK 64-Bit Server VM (build 24.51-b03,mixed mode)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐