JVM 源码分析03

深入分析Object.finalize方法的实现原理

finalize

如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响。

JVM 源码分析03

JVM 源码分析03

JVM 源码分析03

JVM 源码分析03

FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象,如果队列中没有元素,则通过wait方法将该线程挂起,等待被唤醒

如果返回了Finalizer对象,执行对象的runFinalizer()方法,其实可以发现:在runFinalizer()方法中主动捕获了异常,即使在执行finalize方法抛出异常时,也没有关系

JVM 源码分析03

   通过hasBeenFinalized方法判断该对象是否还在链表中,并将该Finalizer对象从链表中删除,这样下次gc时就可以把原对象给回收掉了,最后调用了native方法invokeFinalizeMethod,其中invokeFinalizeMethod方法最终会找到并执行对象的finalize方法。

JVM 源码分析03

JVM 源码分析03

JVM 源码分析03

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

JVM源码分析之Object.wait/notify实现

Object作为java中所有对象的基类,其存在的价值不言而喻,其中wait和notify方法的实现多线程协作提供了保证。

JVM 源码分析03

JVM 源码分析03

 

JVM 源码分析03

代码执行过程分析

1、在多核环境下,线程A和B有可能同时执行monitorenter指令,并获取lock对象关联的monitor,只有一个线程可以和monitor建立关联,假设线程A执行加锁成功;
2、线程B竞争加锁失败,进入等待队列进行等待;
3、线程A继续执行,当执行到wait方法时,会发生什么?wait接口注释:

wait方法会将当前线程放入wait set,等待被唤醒,并放弃lock对象上的所有同步声明,意味着线程A释放了锁,线程B可以重新执行加锁操作,不过又有一个疑问:在线程A的wait方法释放锁,到线程B获取锁,这期间发生了什么?线程B是如何知道线程A已经释放了锁?好迷茫....

4、线程B执行加锁操作成功,对于notify方法,JDK注释:notify方法会选择wait set中任意一个线程进行唤醒;

notifyAll方法的注释:notifyAll方法会唤醒monitor的wait set中所有线程

 

5、执行完notify方法,并不会立马唤醒等待线程,在notify方法后面加一段sleep代码就可以看到效果,如果线程B执行完notify方法之后sleep 5s,在这段时间内,线程B依旧持有monitor,线程A只能继续等待;

那么wait set的线程什么时候会被唤醒?

什么是monitor?

在HotSpot虚拟机中,monitor采用ObjectMonitor实现

JVM 源码分析03

每个线程都有两个ObjectMonitor对象列表,分别为free和used列表,如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。

ObjectMonitor对象中有两个队列:_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表;_owner指向获得ObjectMonitor对象的线程。



JVM 源码分析03

**_WaitSet ** :处于wait状态的线程,会被加入到wait set;
_EntryList:处于等待锁block状态的线程,会被加入到entry set;

JVM 源码分析03

wait方法实现

lock.wait()方法最终通过ObjectMonitor的void wait(jlong millis, bool interruptable, TRAPS);实现:
1、将当前线程封装成ObjectWaiter对象node;
JVM 源码分析03

JVM 源码分析03

JVM 源码分析03

notify方法实现

lock.notify()方法最终通过ObjectMonitor的void notify(TRAPS)实现:
1、如果当前_WaitSet为空,即没有正在等待的线程,则直接返回;
2、通过ObjectMonitor::DequeueWaiter方法,获取_WaitSet列表中的第一个ObjectWaiter节点,实现也很简单。
这里需要注意的是,在jdk的notify方法注释是随机唤醒一个线程,其实是第一个ObjectWaiter节点


JVM 源码分析03

3、根据不同的策略,将取出来的ObjectWaiter节点,加入到_EntryList或则通过Atomic::cmpxchg_ptr指令进行自旋操作cxq,具体代码实现有点长,这里就不贴了,有兴趣的同学可以看objectMonitor::notify方法;

notifyAll方法实现

lock.notifyAll()方法最终通过ObjectMonitor的void notifyAll(TRAPS)实现:
通过for循环取出_WaitSet的ObjectWaiter节点,并根据不同策略,加入到_EntryList或则进行自旋操作。

从JVM的方法实现中,可以发现:notify和notifyAll并不会释放所占有的ObjectMonitor对象,其实真正释放ObjectMonitor对象的时间点是在执行monitorexit指令,一旦释放ObjectMonitor对象了,entry set中ObjectWaiter节点所保存的线程就可以开始竞争ObjectMonitor对象进行加锁操作了。