调试监视器锁

2019-03-15  本文已影响0人  buzzerrookie

为了更深入地理解监视器锁,本文使用gdb调试Hotspot虚拟机展示监视器锁获取与释放的部分执行过程。

调试准备

为了获得Thread类对象的线程ID,内核线程ID与Linux线程ID这三者之间的对应关系,我在hotspot/src/share/vm/runtime/thread.cpp的JavaThread::run函数起始处增加了一行代码,可以按顺序输出JVM中的线程指针、Thread类对象的线程ID、该对象对应的内核线程ID和Linux线程ID。

fprintf(stderr, "JavaThread address: %lx, thread id in Java: %d, LWP: %d, pthread tid: %lx\n", this, java_lang_Thread::thread_id(this->threadObj()), this->_osthread->thread_id(), this->_osthread->pthread_id());

获取监视器锁

获取监视器锁的实验代码如下:

public class MonitorEnter extends Thread {
    private final Object lock;

    public MonitorEnter(Object lock) {
        this.lock = lock;
    }

    public void run() {
        synchronized (lock) {
            try {
                while (true) {
                    Thread.sleep(1000);
                    System.out.println("Thread name: " + Thread.currentThread().getName() + " id: " +
                            Thread.currentThread().getId());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("main thread id: " + Thread.currentThread().getId());
        Object lock = new Object();
        Thread t1 = new MonitorEnter(lock);
        t1.setName("t1");
        System.out.println("t1 id: " + t1.getId());
        Thread t2 = new MonitorEnter(lock);
        t2.setName("t2");
        System.out.println("t2 id: " + t2.getId());
        Thread t3 = new MonitorEnter(lock);
        t3.setName("t3");
        System.out.println("t3 id: " + t3.getId());
        Thread t4 = new MonitorEnter(lock);
        t4.setName("t4");
        System.out.println("t4 id: " + t4.getId());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

gdb调试的交互过程如下:

[suntao@CentOS-VM ~]$ cd openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/
[suntao@CentOS-VM bin]$ gdb ./java
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/suntao/openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/java...done.
(gdb) b /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:546
No source file named /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (/home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:546) pending.
(gdb) r -cp /home/suntao/test/ MonitorEnter
Starting program: /home/suntao/openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/./java -cp /home/suntao/test/ MonitorEnter
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff7feb700 (LWP 11644)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7feb700 (LWP 11644)]
0x00007fffe10002b4 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.3.x86_64 libgcc-4.8.5-36.el7.x86_64 libstdc++-4.8.5-36.el7.x86_64
(gdb) c
Continuing.
[New Thread 0x7ffff4475700 (LWP 11645)]
[New Thread 0x7ffff4374700 (LWP 11646)]
[New Thread 0x7fffde08b700 (LWP 11647)]
[New Thread 0x7fffddf8a700 (LWP 11648)]
JavaThread address: 7ffff0106000, thread id in Java: 2, LWP: 11648, pthread tid: 7fffddf8a700
[New Thread 0x7fffdde89700 (LWP 11649)]
JavaThread address: 7ffff010b800, thread id in Java: 3, LWP: 11649, pthread tid: 7fffdde89700
[New Thread 0x7fffddd88700 (LWP 11650)]
JavaThread address: 7ffff015b000, thread id in Java: 4, LWP: 11650, pthread tid: 7fffddd88700
[New Thread 0x7fffddc87700 (LWP 11651)]
JavaThread address: 7ffff015d000, thread id in Java: 5, LWP: 11651, pthread tid: 7fffddc87700
[New Thread 0x7fffddb86700 (LWP 11652)]
JavaThread address: 7ffff0160800, thread id in Java: 6, LWP: 11652, pthread tid: 7fffddb86700
[New Thread 0x7fffdda85700 (LWP 11653)]
[New Thread 0x7fffdd984700 (LWP 11654)]
JavaThread address: 7ffff016c000, thread id in Java: 7, LWP: 11653, pthread tid: 7fffdda85700
main thread id: 1
t1 id: 8
t2 id: 9
t3 id: 10
t4 id: 11
[New Thread 0x7fffdd883700 (LWP 11655)]
JavaThread address: 7ffff01a9000, thread id in Java: 8, LWP: 11655, pthread tid: 7fffdd883700
[New Thread 0x7fffdd782700 (LWP 11656)]
JavaThread address: 7ffff01ab000, thread id in Java: 9, LWP: 11656, pthread tid: 7fffdd782700
[Switching to Thread 0x7fffdd782700 (LWP 11656)]

Breakpoint 1, ObjectMonitor::EnterI (this=0x7fffc8006368, __the_thread__=0x7ffff01ab000)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:546
546     ObjectWaiter node(Self) ;
(gdb) p _owner
$1 = (void * volatile) 0x7fffdd882780
(gdb) p OwnerIsThread
$2 = 0
(gdb) p _cxq
$3 = (ObjectWaiter * volatile) 0x0
(gdb) p _EntryList
$4 = (ObjectWaiter * volatile) 0x0
(gdb) c
Continuing.
[New Thread 0x7fffdd681700 (LWP 11657)]
Thread name: t1 id: 8
JavaThread address: 7ffff01ad000, thread id in Java: 10, LWP: 11657, pthread tid: 7fffdd681700
[New Thread 0x7fffdd580700 (LWP 11658)]
JavaThread address: 7ffff01af800, thread id in Java: 11, LWP: 11658, pthread tid: 7fffdd580700
[Switching to Thread 0x7fffdd580700 (LWP 11658)]

Breakpoint 1, ObjectMonitor::EnterI (this=0x7fffc8006368, __the_thread__=0x7ffff01af800)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:546
546     ObjectWaiter node(Self) ;
(gdb) p _owner
$5 = (void * volatile) 0x7fffdd882780
(gdb) p OwnerIsThread
$6 = 0
(gdb) p _cxq
$7 = (ObjectWaiter * volatile) 0x7fffdd781480
(gdb) p _cxq->_thread->_osthread->_thread_id
$8 = 11656
(gdb) p _cxq->_next
$9 = (ObjectWaiter * volatile) 0x0
(gdb) c
Continuing.
Thread name: t1 id: 8
[Switching to Thread 0x7fffdd681700 (LWP 11657)]

Breakpoint 1, ObjectMonitor::EnterI (this=0x7fffc8006368, __the_thread__=0x7ffff01ad000)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:546
546     ObjectWaiter node(Self) ;
(gdb) p _owner
$10 = (void * volatile) 0x7fffdd882780
(gdb) p OwnerIsThread
$11 = 0
(gdb) p _cxq
$12 = (ObjectWaiter * volatile) 0x7fffdd57f380
(gdb) p _cxq->_thread->_osthread->_thread_id
$13 = 11658
(gdb) p _cxq->_next->_thread->_osthread->_thread_id
$14 = 11656
(gdb) p _cxq->_next
$15 = (ObjectWaiter * volatile) 0x7fffdd781480
(gdb) p _cxq->_next->_next
$16 = (ObjectWaiter * volatile) 0x0
(gdb) n
547     Self->_ParkEvent->reset() ;
(gdb)
Thread name: t1 id: 8
548     node._prev   = (ObjectWaiter *) 0xBAD ;
(gdb)
549     node.TState  = ObjectWaiter::TS_CXQ ;
(gdb)
557         node._next = nxt = _cxq ;
(gdb)
558         if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
(gdb)
593     if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
(gdb) p _cxq
$17 = (ObjectWaiter * volatile) 0x7fffdd680400
(gdb) p _cxq->_thread->_osthread->_thread_id
$18 = 11657
(gdb) p _cxq->_next
$19 = (ObjectWaiter * volatile) 0x7fffdd57f380
(gdb) p _cxq->_next->_thread->_osthread->_thread_id
$20 = 11658
(gdb) p _cxq->_next->_next
$21 = (ObjectWaiter * volatile) 0x7fffdd781480
(gdb) p _cxq->_next->_next->_thread->_osthread->_thread_id
$22 = 11656
(gdb) p _cxq->_next->_next->_next
$23 = (ObjectWaiter * volatile) 0x0
(gdb) c
Continuing.
Thread name: t1 id: 8
Thread name: t1 id: 8
Thread name: t1 id: 8
^C
Program received signal SIGINT, Interrupt.
[Switching to Thread 0x7ffff7fec740 (LWP 11640)]
0x00007ffff7bc7f47 in pthread_join () from /lib64/libpthread.so.0
(gdb)

上述实验创建的线程如下:

线程名称 Thread类的getId方法返回值 内核线程ID JVM中的线程指针
t1 8 11655 0x7ffff01a9000
t2 9 11656 0x7ffff01ab000
t3 10 11657 0x7ffff01ad000
t4 11 11658 0x7ffff01af800

释放监视器锁

释放监视器锁的实验代码如下:

public class MonitorExit extends Thread {
    private final Object lock;

    public MonitorExit(Object lock) {
        this.lock = lock;
    }

    public void run() {
        synchronized (lock) {
            try {
                Thread.sleep(2000);
                System.out.println("Thread name: " + Thread.currentThread().getName() + " id: " +
                        Thread.currentThread().getId());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("main thread id: " + Thread.currentThread().getId());
        Object lock = new Object();
        Thread t1 = new MonitorExit(lock);
        t1.setName("t1");
        System.out.println("t1 id: " + t1.getId());
        Thread t2 = new MonitorExit(lock);
        t2.setName("t2");
        System.out.println("t2 id: " + t2.getId());
        Thread t3 = new MonitorExit(lock);
        t3.setName("t3");
        System.out.println("t3 id: " + t3.getId());
        Thread t4 = new MonitorExit(lock);
        t4.setName("t4");
        System.out.println("t4 id: " + t4.getId());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

gdb调试的交互过程如下:

[suntao@CentOS-VM ~]$ cd openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/
[suntao@CentOS-VM bin]$ gdb ./java
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/suntao/openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/java...done.
(gdb) b /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:958
No source file named /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (/home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:958) pending.
(gdb) r -cp /home/suntao/test/ MonitorExit
Starting program: /home/suntao/openjdk8/build/linux-x86_64-normal-server-slowdebug/jdk/bin/./java -cp /home/suntao/test/ MonitorExit
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff7feb700 (LWP 11705)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7feb700 (LWP 11705)]
0x00007fffe10002b4 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.3.x86_64 libgcc-4.8.5-36.el7.x86_64 libstdc++-4.8.5-36.el7.x86_64
(gdb) c
Continuing.
[New Thread 0x7ffff4475700 (LWP 11706)]
[New Thread 0x7ffff4374700 (LWP 11707)]
[New Thread 0x7fffde08b700 (LWP 11708)]
[New Thread 0x7fffddf8a700 (LWP 11709)]
JavaThread address: 7ffff0106000, thread id in Java: 2, LWP: 11709, pthread tid: 7fffddf8a700
[New Thread 0x7fffdde89700 (LWP 11710)]
JavaThread address: 7ffff010b800, thread id in Java: 3, LWP: 11710, pthread tid: 7fffdde89700
[New Thread 0x7fffddd88700 (LWP 11711)]
JavaThread address: 7ffff015b000, thread id in Java: 4, LWP: 11711, pthread tid: 7fffddd88700
[New Thread 0x7fffddc87700 (LWP 11712)]
JavaThread address: 7ffff015d000, thread id in Java: 5, LWP: 11712, pthread tid: 7fffddc87700
[New Thread 0x7fffddb86700 (LWP 11713)]
JavaThread address: 7ffff0160800, thread id in Java: 6, LWP: 11713, pthread tid: 7fffddb86700
[New Thread 0x7fffdda85700 (LWP 11714)]
JavaThread address: 7ffff018d000, thread id in Java: 7, LWP: 11714, pthread tid: 7fffdda85700
[New Thread 0x7fffdd984700 (LWP 11715)]
main thread id: 1
t1 id: 8
t2 id: 9
t3 id: 10
t4 id: 11
[New Thread 0x7fffdd883700 (LWP 11716)]
JavaThread address: 7ffff01b9000, thread id in Java: 8, LWP: 11716, pthread tid: 7fffdd883700
[New Thread 0x7fffdd782700 (LWP 11717)]
JavaThread address: 7ffff01bb000, thread id in Java: 9, LWP: 11717, pthread tid: 7fffdd782700
[New Thread 0x7fffdd681700 (LWP 11718)]
JavaThread address: 7ffff01bd000, thread id in Java: 10, LWP: 11718, pthread tid: 7fffdd681700
[New Thread 0x7fffdd580700 (LWP 11719)]
JavaThread address: 7ffff01bf800, thread id in Java: 11, LWP: 11719, pthread tid: 7fffdd580700
Thread name: t1 id: 8
[Switching to Thread 0x7fffdd883700 (LWP 11716)]

Breakpoint 1, ObjectMonitor::exit (this=0x7fffc8002218, not_suspended=true, __the_thread__=0x7ffff01b9000)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:958
958    if (THREAD != _owner) {
(gdb) p _owner
$1 = (void * volatile) 0x7fffdd882500
(gdb) p OwnerIsThread
$2 = 0
(gdb) p _cxq
$3 = (ObjectWaiter * volatile) 0x7fffdd57f600
(gdb) p _cxq->_thread->_osthread->_thread_id
$4 = 11719
(gdb) p _cxq->_next
$5 = (ObjectWaiter * volatile) 0x7fffdd680580
(gdb) p _cxq->_next->_thread->_osthread->_thread_id
$6 = 11718
(gdb) p _cxq->_next->_next
$7 = (ObjectWaiter * volatile) 0x7fffdd781300
(gdb) p _cxq->_next->_next->_thread->_osthread->_thread_id
$8 = 11717
(gdb) p _cxq->_next->_next->_next
$9 = (ObjectWaiter * volatile) 0x0
(gdb) p _EntryList
$10 = (ObjectWaiter * volatile) 0x0
(gdb) n
959      if (THREAD->is_lock_owned((address) _owner)) {
(gdb)
964        assert (_recursions == 0, "invariant") ;
(gdb)
965        _owner = THREAD ;
(gdb)
966        _recursions = 0 ;
(gdb)
967        OwnerIsThread = 1 ;
(gdb) b 1103
Breakpoint 2 at 0x7ffff68e0b01: file /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp, line 1103.
(gdb) c
Continuing.

Breakpoint 2, ObjectMonitor::exit (this=0x7fffc8002218, not_suspended=true, __the_thread__=0x7ffff01b9000)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:1103
1103          if (QMode == 2 && _cxq != NULL) {
(gdb) p Knob_QMode
$11 = 0
(gdb) p _cxq
$12 = (ObjectWaiter * volatile) 0x7fffdd57f600
(gdb) p _EntryList
$13 = (ObjectWaiter * volatile) 0x0
(gdb) n
1114          if (QMode == 3 && _cxq != NULL) {
(gdb)
1152          if (QMode == 4 && _cxq != NULL) {
(gdb)
1187          w = _EntryList  ;
(gdb)
1188          if (w != NULL) {
(gdb) p w
$14 = (ObjectWaiter *) 0x0
(gdb) n
1207          w = _cxq ;
(gdb) p _cxq
$15 = (ObjectWaiter * volatile) 0x7fffdd57f600
(gdb) n
1208          if (w == NULL) continue ;
(gdb) p w
$16 = (ObjectWaiter *) 0x7fffdd57f600
(gdb) n
1214              assert (w != NULL, "Invariant") ;
(gdb)
1215              ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
(gdb) n
1216              if (u == w) break ;
(gdb)
1221          assert (w != NULL              , "invariant") ;
(gdb)
1222          assert (_EntryList  == NULL    , "invariant") ;
(gdb)
1233          if (QMode == 1) {
(gdb)
1252             _EntryList = w ;
(gdb)
1253             ObjectWaiter * q = NULL ;
(gdb) p _EntryList
$17 = (ObjectWaiter * volatile) 0x7fffdd57f600
(gdb) n
1255             for (p = w ; p != NULL ; p = p->_next) {
(gdb)
1256                 guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
(gdb)
1257                 p->TState = ObjectWaiter::TS_ENTER ;
(gdb)
1258                 p->_prev = q ;
(gdb)
1259                 q = p ;
(gdb)
1255             for (p = w ; p != NULL ; p = p->_next) {
(gdb)
1256                 guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
(gdb)
1257                 p->TState = ObjectWaiter::TS_ENTER ;
(gdb)
1258                 p->_prev = q ;
(gdb)
1259                 q = p ;
(gdb)
1255             for (p = w ; p != NULL ; p = p->_next) {
(gdb)
1256                 guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
(gdb)
1257                 p->TState = ObjectWaiter::TS_ENTER ;
(gdb)
1258                 p->_prev = q ;
(gdb)
1259                 q = p ;
(gdb)
1255             for (p = w ; p != NULL ; p = p->_next) {
(gdb)
1269          if (_succ != NULL) continue;
(gdb)
1271          w = _EntryList  ;
(gdb)
1272          if (w != NULL) {
(gdb)
1273              guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
(gdb)
1274              ExitEpilog (Self, w) ;
(gdb) p w->_thread->_osthread->_thread_id
$18 = 11719
(gdb) c
Continuing.
[Thread 0x7fffdd883700 (LWP 11716) exited]
Thread name: t4 id: 11
[Switching to Thread 0x7fffdd580700 (LWP 11719)]

Breakpoint 1, ObjectMonitor::exit (this=0x7fffc8002218, not_suspended=true, __the_thread__=0x7ffff01bf800)
    at /home/suntao/openjdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp:958
958    if (THREAD != _owner) {
(gdb) p _owner
$19 = (void * volatile) 0x7ffff01bf800
(gdb)

上述实验创建的线程如下:

线程名称 Thread类的getId方法返回值 内核线程ID JVM中的线程指针
t1 8 11716 0x7ffff01b9000
t2 9 11717 0x7ffff01bb000
t3 10 11718 0x7ffff01bd000
t4 11 11719 0x7ffff01bf800
上一篇 下一篇

猜你喜欢

热点阅读