When I first learned Java, I had this suspicion, but has not been validated; recently in Oschina to see someone in answer to the question also said so, and began a look at the idea of the--java.lang.object.hashcode () return value is not the object memory address.
(Incidentally, a review of JNI) hashcode contract
Speaking of this question, everyone's first reaction must be the same as me-to check the source of Object.hashcode, but open source, see is this (Oracle JDK 8):
/** * Returns A hash code value for the object.
This are * supported for the benefit of a hash tables such as those provided by * {@link java.util.HashMap}. * <p> * The general contract of {@code hashcode} are: * <ul> * <li>whenever it is in
Voked on the same object more than once during * execution of a Java application, the {@code Hashcode} method * Must consistently return to the same integer, provided no information * used in {@code equals} comparison
s on the object is modified. * This integer need not remain consistent from one execution of ' a * application to another execution of the
Same application. * <li>if two objects are equal according to the {@code equals (Object)} * method, then calling the {@code ha
Shcode the two objects must produce the same integer result. * <li>it is <em>not</em> required IF two objects are unequal * According to the {@link java.lang.object#equals (java.lang.Object)} * method, Then calling the {@code Hashcode} method to each of the * Two objects must produce distinct integer results. However, the * programmer should be aware this producing distinct integer results * for unequal objects
May improve the performance of hash tables. * </ul> * <p> * As much as are reasonably practical, the Hashcode method defined by * class {@co De Object} does return distinct integers for distinct * objects. (This is typically implemented by converting the internal * address of the object in an integer, but this implement ation * Technique is isn't required by the * java™
Programming language.)
* * @return A hash code value for this object. * @see java.lang.object#equals (java.lang.Object) * @see Java.lang.system#identityhashcode/PubLIC native int hashcode ();
Object.hashcode is a native method that does not see the source code (Java, Oracle JDK is invisible, openjdk or other open source JRE is able to find the corresponding C + + code).
The above note indicates some of the contracts (contract) that Object.hashcode () should follow in the JRE (Java Runtime Library):
(PS: The so-called contract is certainly agreed by all, the JVM manufacturers will follow)
Consistency (consistent), which must consistently return the same integer to the same object during one execution of the program.
If two objects are compared by Equals (object) and the result is equal, then calling the Hashcode method on the two objects individually should produce the same integer result. (PS: Here equals and hashcode are all of the object class)
If two objects are compared through java.lang.Object.equals (Java.lang.Ojbect), the results are not equal, and there is no guarantee that the two objects are called hashcode and two different integers are returned.
In fact, the classes in the Java.lang package are all JRE required, and belong to the Run-time library (Runtime Library), which is why many of the JRE's class files are packaged into Rt.jar (should be shorthand for Runtime).
These run-time libraries are generally published with JDK/JRE, so the answer to the different JRE environment is not necessarily the same.
Given the relatively complex object.hashcode of the specific JVM manufacturer, the first question is explored in a different way.
Finally, we will find some object.hashcode of the open source JRE to the specific implementation of the brief analysis. how to get the object memory address in Java.
Do not see Object.hashcode source code, in turn, we can get the object's memory address and object.hashcode comparison, can also draw conclusions.
To verify this problem naturally requires a way to get the object's memory address, but Java itself does not provide a similar approach; that's why I didn't validate the problem when I was learning java.
"Memory Address" is not available in Java, but it is easy to get in C + +. So, we thought--through JNI let Java code call a section of C + + code to get the object memory address.
One more thing you might want to consider here--what type of Java can I use to put down the C + + pointer.
On a 64-bit machine, the pointer to C + + is 8 bytes; 32 bits are 4 bytes.
Well (⊙_⊙) ~ Anyway, Java Long is 8 bytes, enough ~ The java--interface and test
Let's say we've got a Nativeutils.java:
Class Nativeutils {public
native static long Getnativepointer (Object o);
}
And the Getnativepointer method has been implemented.
Then the question raised at the beginning of the validation becomes exceptionally simple:
Class Testnativeutils {public
static void Main (String args[]) {
object o = new Object ();
Long nptr = Nativeutils.getnativepointer (o);
Long hash = O.hashcode ();
System.out.println (String.Format ("hash:%x, nptr:%x", hash, nptr));
}
The method of realizing native in c++--
All right, just do it, and now it's the native Getnativepointer.
Generate the corresponding. h file with Javah:
$ Javah Nativeutils
The execution of the command became NATIVEUTILS.H:
/* Do isn't EDIT this file-it is machine generated/*
#include <jni.h>/
* Header for class nativeutils */
#ifndef _included_nativeutils
#define _included_nativeutils
#ifdef __cplusplus
extern "C" {
# endif
*
* Class: nativeutils
* method: getnativepointer
* Signature: (ljava/lang/ Object;) J
*
/jniexport jlong jnicall java_nativeutils_getnativepointer
(jnienv *, Jclass, jobject);
#ifdef __cplusplus
}
#endif
#endif
This java_nativeutils_getnativepointer is then implemented, and the file is named nativeutils.cc:
#include "NativeUtils.h"
jniexport jlong jnicall
java_nativeutils_getnativepointer (jnienv *env, Jclass Clazz, Jobject o)
{return
reinterpret_cast<jlong> (o);
}
Compile as Dynamic library:
$ g++-shared-o libnative-utils.so nativeutils.cc
The error may be due to jni.h not found:
Nativeutils.h:2:17:fatal error:jni.h:no such file or directory
Find jni.h under the JDK installation directory:
user@host:/usr/lib/jvm/java-7-openjdk-amd64$ find. -name jni.h
./include/jni.h
Once you know the jni.h path, add the-I option to the compilation command to compile again:
$ g++-shared-i/usr/lib/jvm/java-7-openjdk-amd64/include/-o libnative-utils.so nativeutils.cc
OK, the compilation was successful and the libnative-utils.so file was generated. run in shell--the shell environment
Below let Testnativeutils in the shell environment to execute.
First, compile nativeutils and testnativeutils:
$ Javac Nativeutils.java
$ Javac Testnativeutils.java
Nativeutils.class and Testnativeutils.class are generated separately.
All right, just one more.-Execute class file:
$ Java testnativeutils
There was a mistake:
Exception in thread ' main ' Java.lang.UnsatisfiedLinkError:NativeUtils.getNativePointer (ljava/lang/object;) J at
Nativeutils.getnativepointer (Native method) at
Testnativeutils.main (testnativeutils.java:5)
load dynamic libraries into our programs
So far, our Java code has not implemented Nativeutils.getnativepointer, so there will be errors above.
You must load our compiled dynamic library before calling Nativeutils.getnativepointer. A static code block can be used to ensure that the load is completed before the call; Modified nativeutils:
Class Nativeutils {
static {
system.loadlibrary ("Native-utils");
}
Public native static Long Getnativepointer (Object o);
}
let the JVM find the dynamic library
Compile and execute again:
$ Javac Nativeutils.java
$ Java testnativeutils
There are mistakes, but it is not the same as just now:
Exception in thread ' main ' Java.lang.UnsatisfiedLinkError:no native-utils in Java.library.path at
Java.lang.ClassLoader.loadLibrary (classloader.java:1886) at
java.lang.Runtime.loadLibrary0 (runtime.java:849 At
java.lang.System.loadLibrary (system.java:1088) at
nativeutils.<clinit> (nativeutils.java:3) At
Testnativeutils.main (testnativeutils.java:5)
This error is: The native-utils is not found in Java.library.path and can be added to the Java.library.path on the javac command-d parameter to be able to find native-utils:
$ java-djava.library.path=. Testnativeutils
Hash:4f5f1ace, nptr:7f223a5fb958 all in one--makefile
The above commands can be written in a makefile and can be implemented "one-click execution":
Test:runtest
all:libnative-utils.so
jni_include=/usr/lib/jvm/java-7-openjdk-amd64/include
NativeUtils.class:NativeUtils.java
javac Nativeutils.java
TestNativeUtils.class:TestNativeUtils.java
Javac Testnativeutils.java
Nativeutils.h:nativeutils.java
javah-jni nativeutils
libnative-utils.so:nativeutils.cc NativeUtils.h
g++-shared-i${jni_include}-o libnative-utils.so nativeutils.cc
runtest:TestNativeUtils.class libnative-utils.so
@echo "Run test:"
java-djava.library.path=. Testnativeutils clean
:
rm-v *.class *.so *.h
concrete implementation of several JRE
Speaking of the Open source Java environment, the first thing you can think of is OpenJDK and Android, which are briefly analyzed below. hashcode on Android
The Android Object.hashcode differs slightly from the Oracle JDK:
private transient int shadow$_monitor_;
public int hashcode () {
int lockword = shadow$_monitor_;
Final int lockwordmask = 0xc0000000; Top 2 bits.
Final int lockwordstatehash = 0x80000000; Top 2 bits are value 2 (kstatehash).
if ((Lockword & lockwordmask) = = Lockwordstatehash) {return
Lockword & ~lockwordmask;//
} return
S Ystem.identityhashcode (this);
}
(PS: Estimated shadow$_monitor_ should be a cache of hash value, the first time need to calculate, the future is not calculated)
At the first execution, the Shadow$_monitor_ value is 0, and the System.identityhashcode is called:
public static native int Identityhashcode (Object anobject);
And it's a native method, corresponding to the code (JAVA_LANG_SYSTEM.CC):
Static Jint System_identityhashcode (jnienv* env, Jclass, Jobject javaobject) {
if (unlikely (javaobject = = nullptr)) { return
0;
}
Scopedfastnativeobjectaccess SOA (env);
mirror::object* o = SOA. Decode<mirror::object*> (javaobject);
Return static_cast<jint> (O->identityhashcode ());
}
Obviously, the key here is Mirror::object::identityhashcode:
int32_t Object::identityhashcode () const {mirror::object* current_this = const_cast<mirror::object*> (this);
while (true) {Lockword LW = Current_this->getlockword (false); Switch (LW. GetState ()) {case lockword::kunlocked: {//kunlocked is Lockword's default state value//Try to compare and swap in a n
EW Hash, if we succeed we'll return the hash on the next//loop iteration.
Lockword Hash_word (Lockword::fromhashcode (Generateidentityhashcode ())); Dcheck_eq (Hash_word.
GetState (), Lockword::khashcode); if (const_cast<object*> (this)->caslockwordweakrelaxed (LW, Hash_word)) {return Hash_word.
GetHashCode ();
} break; Case lockword::kthinlocked: {//Inflate the thin lock to a monitor and stick the hash code inside of the Monitor.
May//Fail spuriously.
thread* self = thread::current ();
Stackhandlescope<1> HS (self); Handle<mirror::object&gT H_this (HS.
Newhandle (Current_this));
Monitor::inflatethinlocked (self, h_this, LW, Generateidentityhashcode ());
A GC may have occurred if we switched to kblocked. Current_this = H_this.
Get ();
Break
Case lockword::kfatlocked: {//already inflated, return the has stored in the monitor. monitor* monitor = LW.
Fatlockmonitor ();
Dcheck (monitor!= nullptr);
return Monitor->gethashcode (); Case Lockword::khashcode: {//Call return LW later.
GetHashCode (); } default: {LOG (FATAL) << "Invalid state during Hashcode" << LW.
GetState ();
Break
}} LOG (FATAL) << "unreachable";
return 0; }
This code can be seen--art Object.hashcode really have cache, for the same ojbect, the first call Object.hashcode will perform the actual calculation and recorded in the cache, and then directly from the cache out.
The real computing hashcode is generateidentityhashcode:
int32_t Object::generateidentityhashcode () {
static Atomicinteger seed (987654321 + std::time (nullptr));
int32_t Expected_value, New_value;
do {
expected_value = static_cast<uint32_t> (seed. Loadrelaxed ());
New_value = Expected_value * 1103515245 + 12345;
} while ((Expected_value & lockword::khashmask) = = 0 | |
! Seed. Compareexchangeweakrelaxed (Expected_value, New_value));
return Expected_value & Lockword::khashmask;
}
As you can see from Generateidentityhashcode, the return value of art's object.hashcode is not directly related to the address of the object. hashcode on OpenJDK
OPENJDK Project Home: openjdk.java.net
The concrete realization and brief analysis of Hashcode on TODO:OPENJDK
Object.c
static jninativemethod methods[] = {{"Hashcode", "() I", (void *) &j Vm_ihashcode}, {"Wait", "(J) v", (void *) &jvm_monitorwait}, {"Notify", "() v", (void *) &jvm_monitornotify}, {"Notifyall", "() V", (void *) &jvm_monitornot
Ifyall}, {"Clone", "() Ljava/lang/object;", (void *) &jvm_clone},}; Jniexport void Jnicall java_java_lang_object_registernatives (jnienv *env, Jclass cls) {(*env)->registernatives (env
, CLS, methods, sizeof (methods)/sizeof (methods[0)); } jniexport jclass Jnicall java_java_lang_object_getclass (jnienv *env, jobject this) {if (this = NULL) {JN
U_thrownullpointerexception (env, NULL);
return 0;
else {return (*env)->getobjectclass (env, this); }
}
This code indicates that the Object.hashcode corresponding C function is Jvm_ihashcode, and the following needs to find the Jvm_ihashcode code 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 was null return
handle = = NULL 0:objectsynchronizer::fasthashcode (THREAD, Jnihandles::resolve_n On_null (handle));
Jvm_end
Here is just a wrapper, the actual calculation hashcode is Objectsynchronizer::fasthashcode, located in Synchronizer.cpp:
intptr_t Objectsynchronizer::fasthashcode (Thread * Self, oop obj) {if (usebiasedlocking) {//Note:many places th Roughout The JVM does not expect a safepoint//is taken here, in particular most operations on Perm GEN//Objec Ts.
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 are to avoid useless calls to//thread-local storage. if (Obj->mark ()->has_bias_pattern ()) {//Box and unbox the raw reference just in case we cause a STW safepoin
T. Handle hobj (Self, obj);
Relaxing assertion for bug 6320749.
ASSERT (universe::verify_in_progress () | | !
Safepointsynchronize::is_at_safepoint (), "biases should is seen by VM thread");
Biasedlocking::revoke_and_rebias (Hobj, False, Javathread::current ()); obj = Hobj ();
ASSERT (!obj->mark ()->has_bias_pattern (), "biases should is revoked by");
}//Hashcode () is a heap mutator ...//relaxing assertion for bug 6320749.
ASSERT (universe::verify_in_progress () | | !
Safepointsynchronize::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//uses (machine Word version) atomic operation to install the hash test = (mark
OOP) 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 and but it does not worth the complexity.
else if (Mark->has_monitor ()) {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 the current thread, Check if the displaced if (hash) {//header contains hash code return hash;
}//WARNING://The displaced header is strictly immutable. It can not is 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) would be asynchronously//read by other threads during the inflate () F
Unction.
Any change to stack may isn't propagate to the 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 (); Remove Cache if (hash = = 0) {hash = Get_next_hash (Self, obj);//Actual Calculation temp = Mark->copy_set_hash (hash);//merge hash code into header assert (Temp->is_neutral (), "invariant"); Test = (Markoop) atomic<