Object类源码分析

Object类

Object类是类层次结构的根类。Object类是每一个类的超类。所有对象,包括数组,都实现了这个类的方法。
Object类属于java.lang包,所有类都直接或间接继承Object类,在Jdk1.6版本中Object类共有11个方法。
Object类中有很多native方法,也称为本地方法,具体是用C(C++)在动态库中实现的,然后通过JNI调用。

源码分析

package java.lang;
public class Object {
    private static native void registerNatives();
    static {
        registerNatives();  // 本地方法,对象初始化时自动调用此方法
    }

    // 1. 返回这个Object的运行时Class对象。此对象也被用于该类的static synchronized方法的监视器对象。  
    public final native Class<?> getClass(); 

    // 2. 返回这个对象的哈希值,默认是返回该对象的内存地址。重写此方法可以提高哈希结构的集合的性能,例如HashMap。
    public native int hashCode(); 

    // 3. 对象比较方法,判断当前对象与其它对象是否相等,默认是比较两个对象的地址是否相同。
    // 一般重写equals方法时会同时重写hashCode方法
    public boolean equals(Object obj) {
    return (this == obj);
    }

    // 4.对象克隆方法用于对象的复制,支持复制的类必须要实现Cloneable接口,如果没有实现Cloneable接口,执行clone方法会产生异常。 
    protected native Object clone() throws CloneNotSupportedException; 

    // 5. 返回该对象的字符串表示形式,默认是使用类名+16进制的hashcode值(内存地址),重写此方法可以提高类的可读性。
    public String toString() {  
        return getClass().getName() + "@" + Integer.toHexString(hashCode());  
    } 

    // 6. 线程唤醒(单个),唤醒在此对象监视器上等待的单个线程。可用于线程间相互协作通信。
    public final native void notify();   

    // 7. 线程唤醒(所有),唤醒在此对象监视器上等待的所有线程。  
    public final native void notifyAll(); 

    // 8. 线程等待,本地方法。timeout表示要等待的最长时间(以毫秒为单位)。 
    public final native void wait(long timeout) throws InterruptedException;

    // 9. 线程等待,支持毫微秒修正值,进行四舍五入
    public final void wait(long timeout, int nanos) throws InterruptedException {
        //... nanos为额外时间(单位为毫微秒),取值范围是0~999999,当nanos>=500000,则timeout值加1
        wait(timeout); 
    }

    // 10. 线程等待,与notify方法是相对的方法,wait与nofity两个方法实现线程通信,这两个方法必须在同一个监控器对象的同步代码块中使用。
    public final void wait() throws InterruptedException {
    wait(0);   
    }

    // 11. 类似析构函数,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法,用于释放资源。
    // finalized方法在由GC调用,并且还允许一次再生(标记为finalized后该对象在这个线程中又可达了),可能会拖慢GC速度导致频繁fullGC,甚至直接OOM。
    // finalize的执行是不确定的,既不确定由哪个线程执行,也不确定执行的顺序。
    // 由于finalize方法可能影响GC,因此可以使用PhantomReference虚引用来替代finalize的功能。
    // effective java告诉我们,最好的做法是提供close()方法,并且告知上层应用在不需要该对象时一掉要调用这类接口。
    protected void finalize() throws Throwable { }
}

finalize方法缺陷

  • 重写finalize方法对象的创建过程
    • 创建对象A实例
    • 创建java.lang.ref.Finalizer对象实例F1,F1指向A和一个ReferenceQueue,引用关系:F1—>A,F1—>ReferenceQueue(Finalizer类静态方法创建一个后台线程从queue中执行runFinalizer方法)
    • java.lang.ref.Finalizer的类对象引用F1,这样可以保持F1永远不会被回收,除非解除Finalizer的类对象对F1的引用。
    • 经过上述三个步骤,引用关系:java.lang.ref.Finalizer–>F1–>A,F1–>ReferenceQueue
  • 重写finalize方法对象的GC回收过程
    • 在发生minor gc时,即便一个对象A不被任何其他对象引用,只要它含有override finalize(),就会最终被java.lang.ref.Finalizer类的一个对象F1引用
    • 如果新生代的对象都含有override finalize(),那岂不是无法GC?没错,这就是finalize()的第一个风险所在
    • minor gc会把所有活跃对象以及被java.lang.ref.Finalizer类对象引用的(实际)垃圾对象拷贝到下一个survivor区域,如果拷贝溢 出,就将溢出的数据晋升到老年代,极端情况下,老年代的容量会被迅速填满,于是让人头痛的频繁full gc就离我们不远了
    • 当第一次minor gc中发现一个对象只被java.lang.ref.Finalizer类对象引用时,GC线程会把指向对象A的Finalizer对象F1塞入F1所引用的ReferenceQueue中;
    • java.lang.ref.Finalizer类对象中包含了一个运行级别很低的deamon线程 finalizer来异步地调用这些对象的finalize()方法,调用完之后,java.lang.ref.Finalizer类对象会清除自己对 F1的引用。这样GC线程就可以在下一次minor gc时将对象A回收掉。
    • 可见Finalizer对象的多少也会直接影响minor gc的快慢。
  • finalizer方法的对象回收过程总结下来,有以下三个风险:
    • 如果随便一个finalize()抛出一个异常,finallize线程会终止,很快地会由于ReferenceQueue的不断增长导致OOM
    • finalizer线程运行级别很低,有可能出现finalize速度跟不上对象创建速度,最终可能还是会OOM,实际应用中一般会有富裕的CPU时间,所以这种OOM情况可能不太常出现
    • 含有override finalize()的对象至少要经历两次GC才能被回收,严重拖慢GC速度,运气不好的话直接晋升到老年代,可能会造成频繁的full gc,进而影响这个系统的性能和吞吐率。

参考资料
* Java Object类源码阅读
* java中Object类 源代码详解
* 深入理解java的finalize
* finalize实现原理与由此引发的血案
* Java的Finalizer引发的内存溢出

猜你喜欢

转载自blog.csdn.net/bbirdsky/article/details/55100512