大家好,我是小图灵视界,最近在分析Java8源码,使用的JDK是OpenJDK8,打算将分析源码的笔记都会分享出来,在头条上代码排版比较难看,想要笔记的可以关注并私信我。
Object 源码
位置:java.lang包
Object类是Java中最基本的类,是所有类的根。也就说,所有的类默认都会继承它,包括数组等,都要继承Object中的所有方法。Object类中大多数都是native方法,native就是本地方法,由关键native字修饰,这些方法不在java语言中实现,底层实现是的c/c++代码。主要的native方法如下:
<code>private
static
native
void
registerNatives
()
;public
final
native
Class> getClass();public
native
int
hashCode
()
;protected
native
Objectclone
()
throws
CloneNotSupportedException;public
final
native
void
notify
()
;public
final
native
void
notifyAll
()
;public
final
native
void
wait
(
long
timeout)throws
InterruptedException;/<code>
private static native void registerNatives()
在Object类中,有static代码块,这个静态代码块调用了registerNatives()方法:
<code>private
static
native
void
registerNatives
()
;static
{ registerNatives(); }/<code>
registerNatives方法的作用是加载和注册本地C、C++语言函数,将Java的本地方法与JVM底层的C、C++语言函数对应起来,是连接java语言与底层语言的桥梁,registerNatives方法在其他类中也可能存在,如Class类。Java类的本地方法对应C、C++函数的规则是Java_包名_方法名,包名以下划线分隔,Object类的registerNatives对应着C语言函数是
Java_java_lang_Object_registerNatives,java_lang_Object是java全类名以下划线连接,registerNatives是Java中的方法,其中
Java_java_lang_Object_registerNatives这个C语言函数如下:
<code>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}, };JNIEXPORT
void
JNICALLJava_java_lang_Object_registerNatives
(JNIEnv *env, jclass cls)
{ (*env)->RegisterNatives(env, cls, methods,sizeof
(methods)/sizeof
(methods[0
])); }/<code>
Java_java_lang_Object_registerNatives主要是注册Object类的hashCode、wait、notify、notifyAll、clone等方法,而getClass方法却不在这里加载。在Object类中,这些本地方法不是在Java层面实现的,所有在调用这些方法的时候,是调用了底层语言的具体实现。
在methods[]数组中,有多个Java本地方法对应JVM层面的函数,如Object中hashCode方法对应JVM中的JVM_IHashCode函数,返回的类型是()I,即返回的是整数类型。
public final native Class> getClass()
getClass()方法的作用是返回对象运行时的Class类型,java编译会将java类编译成以.class结尾的文件,这是编译生成的二进制字节码文件,用于JVM加载,Java跨平台是因为使用与平台无关的class二进制字节码文件,可以通过Jjava中的Class类获取对象的构造器、方法、属性、注解等相关信息:
<code>Objectobject
=new Object(); System.out
.println(object
.getClass()); Class> objectClass=object
.getClass(); Constructor>[] constructors=objectClass.getConstructors(); System.out
.println("Object 类的构造器:"
);for
(Constructorconstructor
:constructors){ System.out
.print(constructor
); } Method[] methods=objectClass.getMethods(); System.out
.println("Object 类的方法:"
);for
(Method method:methods){ System.out
.println(method); } Field[] fields=objectClass.getFields(); System.out
.println("Object 类的属性:"
);for
(Field field:fields){ System.out
.println(field); }/<code>
getClass方法对应的底层C语言函数为
Java_java_lang_Object_getClass,具体实现为:
<code>JNIEXPORT jclass JNICALL Java_java_lang_Object_getClass(JNIEnv *env, jobjectthis
) {if
(this
== NULL) { JNU_ThrowNullPointerException(env, NULL);return
0
; }else
{return
(*env)->GetObjectClass(env,this
); } }/<code>
当传入的对象this为null,直接抛出NullPointerException异常,否则调用GetObjectClass函数,GetObjectClass函数如下:
<code>JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj)) JNIWrapper("GetObjectClass"
); DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj); HOTSPOT_JNI_GETOBJECTCLASS_ENTRY(env, obj); Klass* k = JNIHandles::resolve_non_null(obj)->klass(); jclass ret =(jclass) JNIHandles::make_local(env, k->java_mirror()); DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret); HOTSPOT_JNI_GETOBJECTCLASS_RETURN( ret);return
ret; JNI_END/<code>
上述的核心代码为:
<code>Klass* k = JNIHandles::resolve_non_null(obj)->klass(); /<code>
resolve_non_null方法主要作用是根据java对象引用找到JVM中引用对象oop,将Java对象转换为JVM中的引用对象,然后调用klass()方法找到元数据,resolve_non_null的方法为:
<code>inline
oop JNIHandles::resolve_non_null(jobject handle) { assert(handle !=NULL
,"JNI handle should not be null"
); oop result = *(oop*)handle; assert(result !=NULL
,"Invalid value read from jni handle"
); assert(result != badJNIHandle,"Pointing to zapped jni handle area"
); assert(result != deleted_handle(),"Used a deleted global handle."
);return
result; };/<code>
resolve_non_null方法的核心代码是oop result = (oop)handle;这句代码的意思是先将传入的Java对象转为oop实例,((oop*)handle),然后再获取oop的指针,这个指针就是引用对象实例的地址(*(oop*)handle)。然后调用klass()获取对象实例所属的元数据Klass,Klass是指向Class类型的指针。
<code>inline
Klass* oopDesc::klass()const
{if
(UseCompressedClassPointers) {return
Klass::decode_klass_not_null(_metadata._compressed_klass); }else
{return
_metadata._klass; } }/<code>
jclass ret =(jclass) JNIHandles::make_local(env, k->java_mirror())分为两步,先调用Klass的java_mirror()方法,java_mirror方法的作用是返回Klass元数据的镜像(oop),对应着jjava/lang/Class类的实例,ava_mirror方法如下:
<code>//
java/lang/Class instance mirroringthis
class
oop _java_mirror; oop java_mirror() const {return
_java_mirror; }/<code>
最后通过 JNIHandles::make_local处理oop,然后返回处理过后的oop,make_local的代码如下:
<code>jobject JNIHandles::make_local(oop obj) {if
(obj ==NULL
) {return
NULL
; }else
{ Thread* thread = Thread::current(); assert(Universe::heap()->is_in_reserved(obj),"sanity check"
);return
thread->active_handles()->allocate_handle(obj); } }/<code>
在JNIHandles::make_local函数中,当传入的oop实例obj为空时,直接返回空,否则将处理过的结果。
public native int hashCode()
hashCode方法返回对象的哈希码,以整数形式返回。哈希表在java集合中如HashMap、HashSet中被广泛使用,主要作用是为了提高查询效率。hashCode方法一些规定/约定如下:
hashCode方法的使用:
<code>Object
o1=new
Object
();Object
o2=new
Object
();Object
o3=o1; System.out.println("o1的hash的哈希码:"
+ o1.hashCode()); System.out.println("o2的hash的哈希码:"
+ o2.hashCode()); System.out.println("o3的hash的哈希码:"
+ o3.hashCode()); o1的hash的哈希码:399573350
o2的hash的哈希码:463345942
o3的hash的哈希码:399573350
/<code>
o1和o2是不同的对象,hashCode方法产生不同的哈希码, o1与o3是不同的对象,它们的哈希码不一样。hashCode的底层C++实现
<code>static
inline intptr_t get_next_hash(Thread *Self
, oop obj) { intptr_t value =0
;if
(hashCode ==0
) { value = os::random() ; }else
if
(hashCode ==1
) { intptr_t addrBits = cast_from_oop(obj) >>3
; value = addrBits ^ (addrBits >>5
) ^ GVars.stwRandom ; }else
if
(hashCode ==2
) { value =1
; }else
if
(hashCode ==3
) { value = ++GVars.hcSequence ; }else
if
(hashCode ==4
) { value = cast_from_oop(obj) ; }else
{ 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
->_hashStateW = v ; value = v ; } value &= markOopDesc::hash_mask;if
(value ==0
) value =0xBAD
; assert (value != markOopDesc::no_hash,"invariant"
) ; TEVENT (hashCode: GENERATE) ;return
value; }/<code>
get_next_hash函数根据hashCode的值,来采用不同的hashCode计算方法,当hashCode=0时,是OpenJDK6、7的默认实现方法,此类方案返回一个Park-Miller伪随机数生成器生成的随机数;当hashCode=1时,通过将对象的内存地址,做移位运算后与一个随机数进行异或得到结果;当hashCode == 3,返回一个自增序列的当前值;当hashCode == 4时,返回当前对象的内存地址;当hashCode 为其他值时,是openJDK8 的hashCode 方法的默认实现。
openJDK8 的默认hashCode的计算方法是通过和当前线程有关的一个随机数+三个确定值,运用Marsaglia's xorshift scheme随机数算法得到的一个随机数。xorshift算法是通过移位和与或计算,能够在计算机上以极快的速度生成伪随机数序列。算法如下:
<code>unsigned
long
xor128
()
{static
unsigned
long
x=123456789
,y=362436069
,z=521288629
,w=88675123
;unsigned
long
t; t=(xˆ(x11
));x=y;y=z;z=w;return
( w=(wˆ(w>>19
))ˆ(tˆ(t>>8
)) ); }/<code>
Self->hashStateX 是随机数、Self->hashStateY 、Self->hashStateZ、Self->hashStateW 是三个确认的数,分别对应xorshift 算法的x、y、z、w。
在启动JVM时,可以通过设置-XX:hashCode参数,改变默认的hashCode的计算方式。
public boolean equals(Object obj)
<code>public
boolean
equals
(Object obj)
{return
(this
== obj); }/<code>
equals方法判断两个对象是否相等,在这个方法中,直接用this==obj进行比较,返回this和obj比较的结果,“==“符号是比较两个对象是否是同一个对象,比较的是两个对象内存地址是否相等。子类在继承Object类的时候,一般需要重写equals方法,如果不重写,那么就默认父类Object的方法,在JDK源码中,很多对象都重写equals方法,比如String类。
对于非空的对象引用,equals方法有几个性质:
子类在继承Object,重写equals方法时,必须重写hashCode方法,在hashCode约定中,如果两个对象相等,hashCode必须返回相同的哈希码。很多时候,子类在重写父类Object的equals方法时,往往会忘了重写hashCode,当重写了equals但没有重写hashCode方法时,那么该子类无法结合java集合正常运行,因为java集合如HashMap、HashSet等都是基于哈希码进行存储的。
protected native Object clone() throws CloneNotSupportedException;
clone()本地方法,创建并返回此对象的副本,该方法使用protected 修饰,Object不能直接调用clone()方法,会编译错误:
<code>Object
o=new
Object
();Object
o1= o.clone();/<code>
如果想要使用clone()方法,子类必须重写这个方法,并用public修饰。如果子类没有实现clone(),子类默认调用父类的clone方法,如下:
<code>public
class
ObjectCloneTest
{public
static
void
main
(String [] args
) throws CloneNotSupportedException { ObjectCloneTest o=new
ObjectCloneTest(); ObjectCloneTest cloneTest= (ObjectCloneTest) o.clone(); System.out
.println(cloneTest); } }/<code>
但是运行时发生异常:
<code>Exception
in
thread
"main
"java
.lang
.CloneNotSupportedException
:com
.lingheng
.java
.source
.ObjectCloneTest
at
java
.lang
.Object
.clone
(Native
Method
)at
com
.lingheng
.java
.source
.ObjectCloneTest
.main
(ObjectCloneTest
.java
:12)
/<code>
如果子没有实现Cloneable接口,当子类调用clone()方法时,会抛出
CloneNotSupportedException异常。当子类实现Cloneable接口,程序运行会成功:
<code>public
class
ObjectCloneTest
implements
Cloneable
{public
static
void
main
(String [] args)
throws
CloneNotSupportedException { ObjectCloneTest o=new
ObjectCloneTest(); ObjectCloneTest cloneTest= (ObjectCloneTest) o.clone(); System.out.println("打印cloneTest:"
+cloneTest); } } 打印cloneTest:com.lingheng.java.source.ObjectCloneTest@6
d6f6e28/<code>
所以,想要使用clone()方法,除了继承Object外(默认继承),还需要实现Cloneable接口。所有的数组默认是实现了Cloneable接口的,数组的clone()返回数组类型:
<code>public
void
arrayClone
(){ String[] s=new
String[]{"hello"
,"world"
}; System.out
.println(s); System.out
.println(s.clone());int
[] num=new
int
[]{1
,3
}; System.out
.println(num); System.out
.println(num.clone()); } [/<code>
对于任何对象x,具有以下几个约定:
- x.clone() != x 为true
- x.clone().getClass() == x.getClass() 为true,但这不是必须满足的要求,也就是说可能是false。按照约定,返回的对象应该通过调用super.clone来获得。如果一个类和它所有的超类(除了Object) 遵循这个约定,那就会x.clone().getClass() == x.getClass()。
- x.clone().equals(x)通常情况下为true,这不是必须满足的要求。
对于约定1,因为拷贝是创建一个新的对象,所以拷贝的对象和原对象是不相等的。
对于约定2,这个比较好理解,一个类和它所有的超类的clone方法都调用super.clone()获取返回对象,所以这个类的Class个原对象的Claas是一样的。clone返回的类型是Object类,也就是说,在clone中可以返回只要是Object的子类就可以了,这样的话,x.clone().getClass() == x.getClass()的结果为false;
对于约定3,这不是必须满足的条件,当在clone中改变了对象的属性值,那么x.clone().equals(x)的结果就可能不是true了。
分析了Java层面的clone的约定,我们从JVM层面看看clone底层的实现,在registerNatives函数中会加载clone的JVM实现JVM_Clone,JVM_Clone的实现如下:
<code> JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) JVMWrapper("JVM_Clone"
); Handle obj(THREAD, JNIHandles::resolve_non_null(handle));const
KlassHandle klass (THREAD, obj->klass()); JvmtiVMObjectAllocEventCollector oam;if
(obj->is_array()) { guarantee(klass->is_cloneable(),"all arrays are cloneable"
); }else
{ guarantee(obj->is_instance(),"should be instanceOop"
); bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass()); guarantee(cloneable == klass->is_cloneable(),"incorrect cloneable flag"
); } ----------------------------------分割线1
-------------------------------------------------if
(!klass->is_cloneable()) { ResourceMark rm(THREAD); THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); }const
int size = obj->size(); oop new_obj =NULL
;if
(obj->is_array()) {const
int length = ((arrayOop)obj())->length(); new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL); }else
{ new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL); } ----------------------------------分割线2
------------------------------------------------- assert(MinObjAlignmentInBytes >= BytesPerLong,"objects misaligned"
); Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj, (size_t)align_object_size(size) / HeapWordsPerLong); new_obj->init_mark(); BarrierSet* bs = Universe::heap()->barrier_set(); assert(bs->has_write_region_opt(),"Barrier set does not have write_region"
); bs->write_region(MemRegion((HeapWord*)new_obj, size));if
(klass->has_finalizer()) { assert(obj->is_instance(),"should be instanceOop"
); new_obj = InstanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL); }return
JNIHandles::make_local(env, oop(new_obj)); JVM_END/<code>
JVM_Clone的实现过程比较长,我将分割成三部分,分割线1上部分主要是检查拷贝的一些标志是否正确,判断传入的是数组还是对象,在上面我讲过所有的数组都默认实现了Cloneable接口,在这里就可以证明了。然后还判断了传入的对象是否继承Cloneable接口,没有继承的话,就是非法的,这是第一段逻辑。
分割线中间部分的逻辑,主要是申请内存,供拷贝的时候使用。首先先判断是否实现Cloneable接口,如果没有抛出
CloneNotSupportedException异常,然后根据传入的是数组还是对象,调用不同的方法进行申请不同大小的内存。代码的有段注释是”Make shallow object copy “,就是说这里的拷贝是浅拷贝。
最后的逻辑是,做一些检查,最后返回新创建的拷贝对象。因为对象的属性可能被另外一个线程改变,所以进行的是原子性的拷贝,最后创建拷贝的对象进行返回。这些源码的注释很详细,有兴趣的可以具体看看其他的一些逻辑。
public String toString()
oString()返回对象的字符串表示形式,最好所有Object的子类都重写这个方法,Object类的toString方法返回的是"类名@哈希码的十六进制,代码实现如下:
<code>public
StringtoString
()
{return
getClass().getName() +"@"
+ Integer.toHexString(hashCode()); }/<code>
当我们在代码输入使用System.out.println输入对象的时候,会调用这个对象的toString方法,返回的是toString的字符串。一般在重写的toString的过程,返回的字符串要易读的。
Object类的源码先分析到这里,因为分析的笔记会比较长,我将用两篇文章来发表。想要笔记的可以关注并私信我。