JAVA基础篇-深度认识Object对象

知识扫盲

Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.
Object对象是根对象,任何对象都是它的子类,包括数组的父类也是Object。

  • JDK环境:JDK1.8
  • OpenJdk:Openjdk8

感兴趣的朋友需要去下载OpenJdk源码,地址如下。
https://download.java.net/openjdk/jdk8u40/ri/openjdk-8u40-src-b25-10_feb_2015.zip

函数作用分析

函数 用途
registerNatives() 静态方法,用来注册Object类的JNI方法,放在Static{}代码块里面静态初始化了。提供clone(),notity()等JNI方法。
getClass() 返回运行时的字节对象,返回的类对象是由所表示类的静态同步方法锁定的对象。
hashCode() 返回对象的哈希码值,受益于Key-Value容器。
equals(Object other) 指示两个对象的值是否相等,必须满足自反性,对称性,传递性,一致性的特性。
clone() 返回对象的副本,亦可称之为浅度克隆(用于享元设计模式)。
toString() 返回对象的字符串表现形式 。
wait() 当前线程等待,直到另一个线程去唤醒它。
finalize() 当垃圾收集确定不再引用对象时,由对象上的垃圾收集器调用。
notify() 唤醒正在此对象监视器上等待的单个线程。
notifyAll() 唤醒正在此对象监视器上等待的所有线程。

先来看一下注册registerNativeJNI方法的OpenJdk源码,源码位置位于Object.C文件中(文件相對路徑:jdk/src/share/native/java/lang/Object.c)

//Native方法与OpenJdk中的对应关系,值得注意的是这里并没有注册//getClass
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 JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, 
// result is :size() of array
sizeof(methods)/sizeof(methods[0]));
}

应用场景.

1)getClass()->得到运行时的对象,一个字节码对象,受益于反射机制。

  • 先来看一下OpenJdk中的源代码,位置位于openjdk\hotspot\src\share\vm\prims\jni.cpp


//getClass位于Object.c中
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
    //this为空时抛出空指针异常
    if (this == NULL) {
        JNU_ThrowNullPointerException(env, NULL);
        return 0;
    } else {
        return (*env)->GetObjectClass(env, this);
    }
}

JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
  JNIWrapper("GetObjectClass");
#ifndef USDT2
  DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj);
#else /* USDT2 */
  HOTSPOT_JNI_GETOBJECTCLASS_ENTRY(
                                   env, obj);
#endif /* USDT2 */
  /* 根据传入的java对象引用找到引用对象然后找到该对象类型的元数据 */
  Klass* k = JNIHandles::resolve_non_null(obj)->klass();
  /*根据元数据找到其镜像,也就是java程序中的Class对象实例*/
  jclass ret = (jclass) JNIHandles::make_local(env, k->java_mirror());
#ifndef USDT2
  DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret);
#else /* USDT2 */
  HOTSPOT_JNI_GETOBJECTCLASS_RETURN(
                                    ret);
#endif /* USDT2 */
  return ret;
JNI_END
Object a=new Object();
Object b=new Object();
System.out.println(a.getClass()==b.getClass())//输出true
System.out.println(Object.class.getClass()==b.getClass())//输出true
  1. hashCode()->返回一个对象的哈希值,是一个数值。在JAVA中,String的Final特性,并且hashCode有缓存机制(只需要计算一次后面就直接拿cache就好了),多数情况下建议选择String来做为HashMap的Key。
    字符串的哈希值计算公式是:s[0]31^(n-1) + s[1]31^(n-2) + ... + s[n-1]。
//字符串"abc"的哈希值是【96354】
"abc".getBytes()[0]*((int)Math.pow(31,2))+"abc".getBytes()[1]*((int)Math.pow(31,1))+"c".hashCode();

*********************贴一段字符串的计算hash的java代码******************************
//hash是私有变量,计算一次就会被cache.
 int h = hash;                                      
 if (h == 0 && value.length > 0) {                  
     char val[] = value;                            
                                                    
     for (int i = 0; i < value.length; i++) {       
         h = 31 * h + val[i];                       
     }                                              
     hash = h;                                      
 }                                                  
 return h; 
********************************************************************************    

  //贴出HashMap的hash算法
 static final int hash(Object key) {
        int h;
        //这里取对象的hashCode()做了亦或和无符号右移运算
       //看得出来哈希的重要性了吧。
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

补充,我们先来看一下hashCode源码。JVM_IHashCode方法指针在 openjdk\hotspot\src\share\vm\prims\jvm.cpp中定义为:

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))  
  JVMWrapper("JVM_IHashCode");  
  // 句柄为空hashCode返回0
  return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;  
JVM_END 

得知源码在synchronizer.cpp中.

  • hashCode==0 返回一个随机数
  • hashCode==1返回一个Park-Miller伪随机数生成器生成的随机数
  • hashCode==2测试敏感度
  • hashCode==3 返回一个自增长的序列号
  • hashCode==4 此类方案返回当前对象的内存地址
  • 其他 Marsaglia的xor-shift方案,具有特定于线程的状态,OpenJdk将来的版本可能会将这个方案设置为默认方案。
    备注:hashCode是VM参数,我们可以通过-XX:hashCode=4固定它的默认hash算法,不过多数情况下我们都是重写了hashCode()算法的。


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(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(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->_hashStateW = v ;
     value = v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}
//
    }
    // 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()) {
    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;

3)equals(Object other)-> 比较两个对象的值,不过需要注意的是Object的equals里面的比较是"==",建议重写equals()方法,重写比较逻辑。

  • 自反性 x.equals(x)==true
  • 对称性 x.equals(y) 等价于 y.equals(x)
  • 传递性 x.equals(y) y.equals(z)那么x.equals(z)
  • 一致性/持久性 x.eauls(y) 要么恒为真,要么恒为假。
"abc".equlas("abc");//输出true;
"abc".equals(new String("abc"));//输出true
"abc".equlas("abd");//输出false;
"abc".equlas(null);//输出false;
//自反性
String a="a"; a.equals(a)==true;
//chu
{
String a="a"; 
}
{String nvl=null;nvl.equals("other");//将抛出NPE}


******************************************************************************
//贴出String的equals方法
  public boolean equals(Object anObject) {
       //如果被比较的对象与当前对象都是指针指向或地址引用同一个对象的话,立马返回真。
        if (this == instance of this) {
            return true;
        }
       //如果被比较的对象是该对象的实例才比较字符串中的每个字符序列
       //这也是一种通用写法 anObject instance 
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }


4)clone()->克隆出对象的副本,默认是浅度克隆,如果重写的话可以改变它的克隆程度,集合中很多容器都是重写了clone()方法,直接化身成了深度克隆了。所以克隆的程度是相对性的,不过,克隆本质上只有两种方式:值传递克隆和引用传递克隆。

一起看下native方法 位于openjdk\hotspot\src\share\vm\prims\jvm.cpp中 JVM_Clone的实现。


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;

#ifdef ASSERT
  // Just checking that the cloneable flag is set correct
  //数组默认是可被克隆的
  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");
  }
#endif

// Check if class of obj supports the Cloneable interface.
  // All arrays are considered to be cloneable (See JLS 20.1.5)
  if (!klass->is_cloneable()) {
    ResourceMark rm(THREAD);
    THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
  }

  // Make shallow object copy
  const int size = obj->size();
  oop new_obj_oop = NULL;
//数组和对象都是调用CollectedHeap的克隆方法
  if (obj->is_array()) {
    const int length = ((arrayOop)obj())->length();
    new_obj_oop = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
  } else {
    new_obj_oop = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
  }

再看看collectedHeap.inline.hpp里面的分配方法

oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
  debug_only(check_for_valid_allocation_state());
  assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
  assert(size >= 0, "int won't convert to size_t");
  HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
  post_allocation_setup_obj(klass, obj, size);
  NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
  return (oop)obj;
}


oop CollectedHeap::array_allocate(KlassHandle klass,
                                  int size,
                                  int length,
                                  TRAPS) {
  debug_only(check_for_valid_allocation_state());
  assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
  assert(size >= 0, "int won't convert to size_t");
  HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
  post_allocation_setup_array(klass, obj, length);
  NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
  return (oop)obj;
}

接下来让我们一起欣赏java中ArrayList和HashMap实现的克隆写法。

      
        try {
            ArrayList v = (ArrayList) super.clone();
            //这里调用的是Arrays的copy对象方法,其实是浅度克隆
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        
    }
*************************************************************************
//为了证明ArrayList是浅度克隆(复制),我们一起来做个实验
       //源头List[1,2]
        ArrayList sourceList=new ArrayList();
        sourceList.add(1);
        sourceList.add(2);

        ArrayList copiedList= (ArrayList) sourceList.clone();
        copiedList.remove(0);//移除第一个元素
        copiedList.set(0,2);//设置第一个元素
        //源没变,依然是长度为2
        System.out.println("sourceList.size()="+sourceList.size());//输出2
       //复制过来的集合长度减少了
        System.out.println("copiedList.size()="+copiedList.size());//输出1


        System.out.println("copiedList.get(0)="+copiedList.get(0));

*************************************************************************

 HashMap result;
        try {
            //拿到Super克隆过来的对象
            result = (HashMap)super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
          //重置对象状态回到初始化状态
         result.reinitialize();
       //将当前的map实体以创建形式复制并放置到result中
        result.putMapEntries(this, false);
        return result;

  HashMap sourceMap=new HashMap();
 sourceMap.put("1",Integer.valueOf("1"));
 sourceMap.put("2",Integer.valueOf("2"));
//克隆一个Map
  HashMap cloneMap=(HashMap)sourceMap.clone();
 cloneMap.remove("2");//移除2;
System.out.print(sourceMap.get("2")==null);//输出true

5)wait()->会释放对象锁,等待被持有该对象句柄映射的线程来唤醒它。为了能够理解它,我们将它结合notify(),notifyAll()在一块,看个例子。看完之后希望能理解(下面的例子已经极度接近于PV操作,所谓PV,换言之是生产消费者模式)。

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 理解wait()和notify()与notifyAll();
 */
public class ObjectWait {

    boolean wait=true;
    private Object lockObject=new Object();


    public void waitForNotifyMe() throws InterruptedException {

        synchronized (lockObject) {
            while (wait)
            lockObject.wait();

        }
        //打印当前持有该对象的线程的唤醒状态
        System.out.println(Thread.currentThread().getName()+"被唤醒了");

    }

    public void waitForNotifyMe(String name) throws InterruptedException {
        synchronized (lockObject) {
            while (wait)
                lockObject.wait();
        }
        //打印当前持有该对象的线程的唤醒状态
        System.out.println(Thread.currentThread().getName()+"被唤醒了");
    }

    //唤醒一个一个线程
    public void notifyOne() throws InterruptedException {

        synchronized (lockObject) {
            this.wait=false;
            lockObject.notify();
        }
    }


    /**
     * 唤醒所有正在仅持有LockObject对象所的线程
     * @throws InterruptedException
     */
    public void notifyAllWaitThread() throws InterruptedException {

        synchronized (lockObject) {
            this.wait=false;
            lockObject.notifyAll();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        ObjectWait testObjectWait=new ObjectWait();
       Thread holderLockObjectThreadA= new Thread(() -> {
           try {
               testObjectWait.waitForNotifyMe();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       holderLockObjectThreadA.setName("等待被唤醒线程A");
       //启动线程A
       holderLockObjectThreadA.start();

      Thread holderLockObjectThreadB=  new Thread(() -> {
            try {
                testObjectWait.waitForNotifyMe("B");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        holderLockObjectThreadB.setName("等待被唤醒线程B");
        holderLockObjectThreadB.start();

       Thread notifySingleThread= new Thread(() -> {
            try {
                testObjectWait.notifyOne();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        notifySingleThread.setName("唤醒一个线程");
        notifySingleThread.start();

        Thread notifyALlThread=new Thread(() -> {
            try {
                testObjectWait.notifyAllWaitThread();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        notifyALlThread.setName("唤醒所有持有lockObject对象锁的线程");
        notifyALlThread.start();


    }
}

程序的输出结果是:

等待被唤醒线程A被唤醒了
等待被唤醒线程B被唤醒了

6)finalize()->垃圾回收器做清理工作时执行的方法,子类可以继承它来做清理系统资源,释放资源,对象复活等操作(有点像C++析构函数的作用)。这个方法在一定程度上解决内存泄漏问难题,如果当前类重写了finalize方法,且非空,需要把生成的对象封装成Finalizer(java.lang.ref.Finalizer)对象并添加到 Finalizer链表中,对象被GC时,如果是Finalizer对象,会将对象赋值到pending对象,gc时它会先删除掉finalizer中的存储对象的链表映射。
下面分享下JDK的Finalizer部分源代码

   //对象的引用队列
    private static ReferenceQueue queue = new ReferenceQueue<>();

    private static class FinalizerThread extends Thread {
        private volatile boolean running;
        FinalizerThread(ThreadGroup g) {
            super(g, "Finalizer");
        }
        public void run() {
            // in case of recursive call to run()
            if (running)
                return;

            // Finalizer thread starts before System.initializeSystemClass
            // is called.  Wait until JavaLangAccess is available
           //如果VM没有初始化,延时直到VM完成初始化
            while (!VM.isBooted()) {
                // delay until VM completes initialization
                try {
                    VM.awaitBooted();
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
            running = true;
            for (;;) {
                try {
                    //
                    Finalizer f = (Finalizer)queue.remove();
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    }

    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread finalizer = new FinalizerThread(tg);
        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
       //设置守护线程去回收对象的引用,注意就算JAVA对象实现了Finalizer,也有可能出现野指针现象,因为线程可能没执行
        finalizer.setDaemon(true);
        finalizer.start();
    }

我们一起看看下面这个gc时让对象复活的方法。

/**
 *测试finalize
 */
public class Finalizer {

    private static Finalizer holderThis;
  
   private void doCleanWork(){
     //关闭资源
}
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        doCleanWork();
        holderThis = this;
        System.out.println("finalize");
    }

    public static void main(String[] args) throws Throwable {
        holderThis = new Finalizer();
        holderThis = null;
        System.gc();
        //线程休眠一下,保证holderThis能够复活
        Thread.sleep(1);
        System.out.println(holderThis == null);//对象还存在,输出的是false
    }
}

分析下OpenJdk源码中的notify(),wait()方法
请欣赏源代码

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
  JVMWrapper("JVM_MonitorWait");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  JavaThreadInObjectWaitState jtiows(thread, ms != 0);
  if (JvmtiExport::should_post_monitor_wait()) {
    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);

    // The current thread already owns the monitor and it has not yet
    // been added to the wait queue so the current thread cannot be
    // made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT
    // event handler cannot accidentally consume an unpark() meant for
    // the ParkEvent associated with this ObjectMonitor.
  }
  ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END


JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_MonitorNotify");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  ObjectSynchronizer::notify(obj, CHECK);
JVM_END


JVM_ENTRY(void, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_MonitorNotifyAll");
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  ObjectSynchronizer::notifyall(obj, CHECK);
JVM_END

  • 核心就是ObjectWaiter,ObjectMonitor,这两个类在openjdk\hotspot\agent\src\share\classes\sun\jvm\hotspot\runtime\ObjectSynchronizer.java
JAVA基础篇-深度认识Object对象_第1张图片
image.png
JAVA基础篇-深度认识Object对象_第2张图片
image.png

详情请参考:https://www.jianshu.com/p/f4454164c017,这篇大牛的博客写得很好,分析得很透彻。

谢谢您的读阅,如果觉得不错的话,可以点点赞,关注走一波,您的支持是我不断前进的动力~~///(^v^)\~~

你可能感兴趣的:(JAVA基础篇-深度认识Object对象)