Thread Dump是很是有用的診斷Java應用問題的工具。每個Java虛擬機都有及時生成全部線程在某一點狀態的thread-dump的能力,雖然各個 Java虛擬機打印的thread dump略有不一樣,可是 大多都提供了當前活動線程的快照,及JVM中全部Java線程的堆棧跟蹤信息,堆棧信息通常包含完整的類名及所執行的方法,若是可能的話還有源代碼的行數。java
- 能在各類操做系統下使用;
- 能在各類Java應用服務器下使用;
- 能在生產環境下使用而不影響系統的性能;
- 能將問題直接定位到應用程序的代碼行上;
通常當服務器掛起,崩潰或者性能低下時,就須要抓取服務器的線程堆棧(Thread Dump)用於後續的分析。在實際運行中,每每一次 dump的信息,還不足以確認問題。爲了反映線程狀態的動態變化,須要接連屢次作thread dump,每次間隔10-20s,建議至少產生三次 dump信息,若是每次 dump都指向同一個問題,咱們才肯定問題的典型性。算法
操做系統命令獲取ThreadDump數據庫
- ps –ef | grep java
- kill -3
注意:編程
必定要謹慎, 一步不慎就可能讓服務器進程被殺死。kill -9 命令會殺死進程。bash
JVM 自帶的工具獲取線程堆棧服務器
- jps 或 ps –ef | grep java (獲取PID)
- jstack [-l ] | tee -a jstack.log(獲取ThreadDump)
頭部信息:時間,JVM信息markdown
2011-11-02 19:05:06
Full thread dump Java HotSpot(TM) Server VM (16.3-b01 mixed mode):
複製代碼
線程INFO信息塊:多線程
1. "Timer-0" daemon prio=10 tid=0xac190c00 nid=0xaef in Object.wait() [0xae77d000] # 線程名稱:Timer-0;線程類型:daemon;優先級: 10,默認是5; # JVM線程id:tid=0xac190c00,JVM內部線程的惟一標識(經過java.lang.Thread.getId()獲取,一般用自增方式實現)。 # 對應系統線程id(NativeThread ID):nid=0xaef,和top命令查看的線程pid對應,不過一個是10進制,一個是16進制。(經過命令:top -H -p pid,能夠查看該進程的全部線程信息) # 線程狀態:in Object.wait(); # 起始棧地址:[0xae77d000],對象的內存地址,經過JVM內存查看工具,可以看出線程是在哪兒個對象上等待; 2. java.lang.Thread.State: TIMED_WAITING (on object monitor) 3. at java.lang.Object.wait(Native Method) 4. -waiting on <0xb3885f60> (a java.util.TaskQueue) # 繼續wait 5. at java.util.TimerThread.mainLoop(Timer.java:509) 6. -locked <0xb3885f60> (a java.util.TaskQueue) # 已經locked 7. at java.util.TimerThread.run(Timer.java:462) 複製代碼
Java thread statck trace:是上面2-7行的信息。到目前爲止這是最重要的數據,Java stack trace提供了大部分信息來精肯定位問題根源。併發
Java thread statck trace詳解:app
堆棧信息應該逆向解讀:程序先執行的是第7行,而後是第6行,依次類推。
- locked <0xb3885f60> (a java.util.ArrayList)
- waiting on <0xb3885f60> (a java.util.ArrayList)
複製代碼
也就是說對象先上鎖,鎖住對象0xb3885f60,而後釋放該對象鎖,進入waiting狀態。爲啥會出現這樣的狀況呢?看看下面的java代碼示例,就會明白:
synchronized(obj) { ......... obj.wait(); ......... } 複製代碼
如上,線程的執行過程,先用 synchronized 得到了這個對象的 Monitor(對應於 locked <0xb3885f60> )。當執行到 obj.wait(),線程即放棄了 Monitor的全部權,進入 「wait set」隊列(對應於 waiting on <0xb3885f60> )。
在堆棧的第一行信息中,進一步標明瞭線程在代碼級的狀態,例如:
java.lang.Thread.State: TIMED_WAITING (parking)
複製代碼
解釋以下:
|blocked| > This thread tried to enter asynchronized block, but the lock was taken by another thread. This thread isblocked until the lock gets released. |blocked (on thin lock)| > This is the same state asblocked, but the lock in question is a thin lock. |waiting| > This thread calledObject.wait() on an object. The thread will remain there until some otherthread sends a notification to that object. |sleeping| > This thread calledjava.lang.Thread.sleep(). |parked| > This thread calledjava.util.concurrent.locks.LockSupport.park(). |suspended| > The thread's execution wassuspended by java.lang.Thread.suspend() or a JVMTI agent call. 複製代碼
線程的狀態是一個很重要的東西,所以thread dump中會顯示這些狀態,經過對這些狀態的分析,可以得出線程的運行情況,進而發現可能存在的問題。線程的狀態在Thread.State這個枚舉類型中定義:
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } 複製代碼
NEW:
每個線程,在堆內存中都有一個對應的Thread對象。Thread t = new Thread();當剛剛在堆內存中建立Thread對象,尚未調用t.start()方法以前,線程就處在NEW狀態。在這個狀態上,線程與普通的java對象沒有什麼區別,就僅僅是一個堆內存中的對象。
RUNNABLE:
該狀態表示線程具有全部運行條件,在運行隊列中準備操做系統的調度,或者正在運行。 這個狀態的線程比較正常,但若是線程長時間停留在在這個狀態就不正常了,這說明線程運行的時間很長(存在性能問題),或者是線程一直得不得執行的機會(存在線程飢餓的問題)。
BLOCKED:
線程正在等待獲取java對象的監視器(也叫內置鎖),即線程正在等待進入由synchronized
保護的方法或者代碼塊。synchronized用來保證原子性,任意時刻最多隻能由一個線程進入該臨界區域,其餘線程只能排隊等待。
WAITING:
處在該線程的狀態,正在等待某個事件的發生,只有特定的條件知足,才能得到執行機會。而產生這個特定的事件,一般都是另外一個線程。也就是說,若是不發生特定的事件,那麼處在該狀態的線程一直等待,不能獲取執行的機會。好比:
- A線程調用了obj對象的
obj.wait()
方法,若是沒有線程調用obj.notify
或obj.notifyAll,那麼A線程就沒有辦法恢復運行;- 若是A線程調用了
LockSupport.park()
,沒有別的線程調用LockSupport.unpark(A)
,那麼A沒有辦法恢復運行。
TIMED_WAITING:
J.U.C中不少與線程相關類,都提供了限時版本和不限時版本的API。TIMED_WAITING意味着線程調用了限時版本的API,正在等待時間流逝。當等待時間過去後,線程同樣能夠恢復運行。若是線程進入了WAITING狀態,必定要特定的事件發生才能恢復運行;而處在TIMED_WAITING的線程,若是特定的事件發生或者是時間流逝完畢,都會恢復運行。
TERMINATED:
線程執行完畢,執行完run方法正常返回,或者拋出了運行時異常而結束,線程都會停留在這個狀態。這個時候線程只剩下Thread對象了,沒有什麼用了。
Wait on condition:The thread is either sleeping or waiting to be notified by another thread. 該狀態說明它在等待另外一個條件的發生,來把本身喚醒,或者乾脆它是調用了 sleep(n)。
此時線程狀態大體爲如下幾種:
- java.lang.Thread.State: WAITING (parking):一直等那個條件發生;
- java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定時的,那個條件不到來,也將定時喚醒本身。
Waiting for Monitor Entry 和 in Object.wait():The thread is waiting to get the lock for an object (some other thread may be holding the lock). This happens if two or more threads try to execute synchronized code. Note that the lock is always for an object and not for individual methods.
在多線程的JAVA程序中,實現線程之間的同步,就要說說 Monitor。 Monitor是Java中用以實現線程之間的互斥與協做的主要手段,它能夠當作是對象或者Class的鎖。每個對象都有,也僅有一個 Monitor。下面這個圖,描述了線程和 Monitor之間關係,以及線程的狀態轉換圖:
如上圖,每一個Monitor在某個時刻,只能被一個線程擁有,該線程就是 「ActiveThread」,而其它線程都是 「Waiting Thread」,分別在兩個隊列「Entry Set」和「Wait Set」裏等候。在「Entry Set」中等待的線程狀態是「Waiting for monitor entry」,而在「Wait Set」中等待的線程狀態是「in Object.wait()」。
先看「Entry Set」裏面的線程。咱們稱被 synchronized保護起來的代碼段爲臨界區。當一個線程申請進入臨界區時,它就進入了「Entry Set」隊列。對應的 code就像:
synchronized(obj) {
.........
}
複製代碼
這時有兩種可能性:
- 該 monitor不被其它線程擁有, Entry Set裏面也沒有其它等待線程。本線程即成爲相應類或者對象的 Monitor的 Owner,執行臨界區的代碼。
- 該 monitor被其它線程擁有,本線程在 Entry Set隊列中等待。
在第一種狀況下,線程將處於 「Runnable」的狀態,而第二種狀況下,線程 DUMP會顯示處於 「waiting for monitor entry」。以下:
"Thread-0" prio=10 tid=0x08222eb0 nid=0x9 waiting for monitor entry [0xf927b000..0xf927bdb8] at testthread.WaitThread.run(WaitThread.java:39) - waiting to lock <0xef63bf08> (a java.lang.Object) - locked <0xef63beb8> (a java.util.ArrayList) at java.lang.Thread.run(Thread.java:595) 複製代碼
臨界區的設置,是爲了保證其內部的代碼執行的原子性和完整性。可是由於臨界區在任什麼時候間只容許線程串行經過,這和咱們多線程的程序的初衷是相反的。若是在多線程的程序中,大量使用 synchronized,或者不適當的使用了它,會形成大量線程在臨界區的入口等待,形成系統的性能大幅降低。若是在線程 DUMP中發現了這個狀況,應該審查源碼,改進程序。
再看「Wait Set」裏面的線程。當線程得到了 Monitor,進入了臨界區以後,若是發現線程繼續運行的條件沒有知足,它則調用對象(通常就是被 synchronized 的對象)的 wait() 方法,放棄 Monitor,進入 「Wait Set」隊列。只有當別的線程在該對象上調用了 notify() 或者 notifyAll(),「Wait Set」隊列中線程才獲得機會去競爭,可是隻有一個線程得到對象的Monitor,恢復到運行態。在 「Wait Set」中的線程, DUMP中表現爲: in Object.wait()。以下:
"Thread-1" prio=10 tid=0x08223250 nid=0xa in Object.wait() [0xef47a000..0xef47aa38] at java.lang.Object.wait(Native Method) - waiting on <0xef63beb8> (a java.util.ArrayList) at java.lang.Object.wait(Object.java:474) at testthread.MyWaitThread.run(MyWaitThread.java:40) - locked <0xef63beb8> (a java.util.ArrayList) at java.lang.Thread.run(Thread.java:595) 複製代碼
綜上,通常CPU很忙時,則關注runnable的線程,CPU很閒時,則關注waiting for monitor entry的線程。
JDK 5.0 的 Lock
上面提到若是 synchronized和 monitor機制運用不當,可能會形成多線程程序的性能問題。在 JDK 5.0中,引入了 Lock機制,從而使開發者能更靈活的開發高性能的併發多線程程序,能夠替代以往 JDK中的 synchronized和 Monitor的 機制。可是,要注意的是,由於 Lock類只是一個普通類,JVM無從得知 Lock對象的佔用狀況,因此在線程 DUMP中,也不會包含關於 Lock的信息, 關於死鎖等問題,就不如用 synchronized的編程方式容易識別。
顯示BLOCKED狀態
package jstack; public class BlockedState { private static Object object = new Object(); public static void main(String[] args) { Runnable task = new Runnable() { @Override public void run() { synchronized (object) { long begin = System.currentTimeMillis(); long end = System.currentTimeMillis(); // 讓線程運行5分鐘,會一直持有object的監視器 while ((end - begin) <= 5 * 60 * 1000) { } } } }; new Thread(task, "t1").start(); new Thread(task, "t2").start(); } } 複製代碼
先獲取object的線程會執行5分鐘,這5分鐘內會一直持有object的監視器,另外一個線程沒法執行處在BLOCKED狀態:
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode): "DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x1314 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "t2" prio=6 tid=0x27d7a800 nid=0x1350 waiting for monitor entry [0x2833f000] java.lang.Thread.State: BLOCKED (on object monitor) at jstack.BlockedState$1.run(BlockedState.java:17) - waiting to lock <0x1cfcdc00> (a java.lang.Object) at java.lang.Thread.run(Thread.java:662) "t1" prio=6 tid=0x27d79400 nid=0x1338 runnable [0x282ef000] java.lang.Thread.State: RUNNABLE at jstack.BlockedState$1.run(BlockedState.java:22) - locked <0x1cfcdc00> (a java.lang.Object) at java.lang.Thread.run(Thread.java:662) 複製代碼
經過thread dump能夠看到:t2線程確實處在BLOCKED (on object monitor)。waiting for monitor entry 等待進入synchronized保護的區域。
顯示WAITING狀態
package jstack; public class WaitingState { private static Object object = new Object(); public static void main(String[] args) { Runnable task = new Runnable() { @Override public void run() { synchronized (object) { long begin = System.currentTimeMillis(); long end = System.currentTimeMillis(); // 讓線程運行5分鐘,會一直持有object的監視器 while ((end - begin) <= 5 * 60 * 1000) { try { // 進入等待的同時,會進入釋放監視器 object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; new Thread(task, "t1").start(); new Thread(task, "t2").start(); } } 複製代碼
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode): "DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x1734 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "t2" prio=6 tid=0x27d7e000 nid=0x17f4 in Object.wait() [0x2833f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x1cfcdc00> (a java.lang.Object) at java.lang.Object.wait(Object.java:485) at jstack.WaitingState$1.run(WaitingState.java:26) - locked <0x1cfcdc00> (a java.lang.Object) at java.lang.Thread.run(Thread.java:662) "t1" prio=6 tid=0x27d7d400 nid=0x17f0 in Object.wait() [0x282ef000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x1cfcdc00> (a java.lang.Object) at java.lang.Object.wait(Object.java:485) at jstack.WaitingState$1.run(WaitingState.java:26) - locked <0x1cfcdc00> (a java.lang.Object) at java.lang.Thread.run(Thread.java:662) 複製代碼
能夠發現t1和t2都處在WAITING (on object monitor),進入等待狀態的緣由是調用了in Object.wait()。經過J.U.C包下的鎖和條件隊列,也是這個效果,你們能夠本身實踐下。
顯示TIMED_WAITING狀態
package jstack; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TimedWaitingState { // java的顯示鎖,相似java對象內置的監視器 private static Lock lock = new ReentrantLock(); // 鎖關聯的條件隊列(相似於object.wait) private static Condition condition = lock.newCondition(); public static void main(String[] args) { Runnable task = new Runnable() { @Override public void run() { // 加鎖,進入臨界區 lock.lock(); try { condition.await(5, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } // 解鎖,退出臨界區 lock.unlock(); } }; new Thread(task, "t1").start(); new Thread(task, "t2").start(); } } 複製代碼
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode): "DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x169c waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "t2" prio=6 tid=0x27d7d800 nid=0xc30 waiting on condition [0x2833f000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x1cfce5b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2116) at jstack.TimedWaitingState$1.run(TimedWaitingState.java:28) at java.lang.Thread.run(Thread.java:662) "t1" prio=6 tid=0x280d0c00 nid=0x16e0 waiting on condition [0x282ef000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x1cfce5b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2116) at jstack.TimedWaitingState$1.run(TimedWaitingState.java:28) at java.lang.Thread.run(Thread.java:662) 複製代碼
能夠看到t1和t2線程都處在java.lang.Thread.State: TIMED_WAITING (parking),這個parking表明是調用的JUC下的工具類,而不是java默認的監視器。
- 一個請求過程當中屢次dump;
- 對比屢次dump文件的runnable線程,若是執行的方法有比較大變化,說明比較正常。若是在執行同一個方法,就有一些問題了;
- 使用命令:
top -H -p pid
(pid爲被測系統的進程號),找到致使CPU高的線程ID,對應thread dump信息中線程的nid,只不過一個是十進制,一個是十六進制;- 在thread dump中,根據top命令查找的線程id,查找對應的線程堆棧信息;
進行dump,查看是否有不少thread struck在了i/o、數據庫等地方,定位瓶頸緣由;
屢次dump,對比是否全部的runnable線程都一直在執行相同的方法,若是是的,恭喜你,鎖住了!
死鎖常常表現爲程序的停頓,或者再也不響應用戶的請求。從操做系統上觀察,對應進程的CPU佔用率爲零,很快會從top或prstat的輸出中消失。
好比在下面這個示例中,是個較爲典型的死鎖狀況:
"Thread-1" prio=5 tid=0x00acc490 nid=0xe50 waiting for monitor entry [0x02d3f000 ..0x02d3fd68] at deadlockthreads.TestThread.run(TestThread.java:31) - waiting to lock <0x22c19f18> (a java.lang.Object) - locked <0x22c19f20> (a java.lang.Object) "Thread-0" prio=5 tid=0x00accdb0 nid=0xdec waiting for monitor entry [0x02cff000 ..0x02cff9e8] at deadlockthreads.TestThread.run(TestThread.java:31) - waiting to lock <0x22c19f20> (a java.lang.Object) - locked <0x22c19f18> (a java.lang.Object) 複製代碼
在 JAVA 5中增強了對死鎖的檢測。線程 Dump中能夠直接報告出 Java級別的死鎖,以下所示:
Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x0003f334 (object 0x22c19f18, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x0003f314 (object 0x22c19f20, a java.lang.Object), which is held by "Thread-1" 複製代碼
熱鎖,也每每是致使系統性能瓶頸的主要因素。其表現特徵爲:因爲多個線程對臨界區,或者鎖的競爭,可能出現:
- 頻繁的線程的上下文切換:從操做系統對線程的調度來看,當線程在等待資源而阻塞的時候,操做系統會將之切換出來,放到等待的隊列,當線程得到資源以後,調度算法會將這個線程切換進去,放到執行隊列中。
- 大量的系統調用:由於線程的上下文切換,以及熱鎖的競爭,或者臨界區的頻繁的進出,均可能致使大量的系統調用。
- 大部分CPU開銷用在「系統態」:線程上下文切換,和系統調用,都會致使 CPU在 「系統態 」運行,換而言之,雖然系統很忙碌,可是CPU用在 「用戶態 」的比例較小,應用程序得不到充分的 CPU資源。
- 隨着CPU數目的增多,系統的性能反而降低。由於CPU數目多,同時運行的線程就越多,可能就會形成更頻繁的線程上下文切換和系統態的CPU開銷,從而致使更糟糕的性能。
上面的描述,都是一個 scalability(可擴展性)不好的系統的表現。從總體的性能指標看,因爲線程熱鎖的存在,程序的響應時間會變長,吞吐量會下降。
那麼,怎麼去了解 「熱鎖 」出如今什麼地方呢?
一個重要的方法是 結合操做系統的各類工具觀察系統資源使用情況,以及收集Java線程的DUMP信息,看線程都阻塞在什麼方法上,瞭解緣由,才能找到對應的解決方法。
JVM運行過程當中產生的一些比較重要的線程羅列以下:
線程名稱 | 所屬 | 解釋說明 |
---|---|---|
Attach Listener | JVM | Attach Listener 線程是負責接收到外部的命令,而對該命令進行執行的並把結果返回給發送者。一般咱們會用一些命令去要求JVM給咱們一些反饋信息,如:java -version、jmap、jstack 等等。 若是該線程在JVM啓動的時候沒有初始化,那麼,則會在用戶第一次執行JVM命令時,獲得啓動。 |
Signal Dispatcher | JVM | 前面提到Attach Listener 線程的職責是接收外部JVM命令,當命令接收成功後,會交給signal dispather 線程去進行分發到各個不一樣的模塊處理命令,而且返回處理結果。signal dispather 線程也是在第一次接收外部JVM命令時,進行初始化工做。 |
CompilerThread0 | JVM | 用來調用JITing ,實時編譯裝卸class 。 一般,JVM會啓動多個線程來處理這部分工做,線程名稱後面的數字也會累加,例如:CompilerThread1 。 |
Concurrent Mark-Sweep GC Thread | JVM | 併發標記清除垃圾回收器(就是一般所說的CMS GC)線程, 該線程主要針對於老年代垃圾回收。ps:啓用該垃圾回收器,須要在JVM啓動參數中加上:-XX:+UseConcMarkSweepGC 。 |
DestroyJavaVM | JVM | 執行main() 的線程,在main執行完後調用JNI中的 jni_DestroyJavaVM() 方法喚起DestroyJavaVM 線程,處於等待狀態,等待其它線程(Java線程和Native線程)退出時通知它卸載JVM。每一個線程退出時,都會判斷本身當前是不是整個JVM中最後一個非deamon線程 ,若是是,則通知DestroyJavaVM 線程卸載JVM。 |
Finalizer Thread | JVM | 這個線程也是在main線程以後建立的,其優先級爲10,主要用於在垃圾收集前,調用對象的finalize() 方法;關於Finalizer線程的幾點:1) 只有當開始一輪垃圾收集時,纔會開始調用finalize() 方法;所以並非全部對象的finalize() 方法都會被執行;2) 該線程也是daemon 線程,所以若是虛擬機中沒有其餘非daemon線程,無論該線程有沒有執行完finalize() 方法,JVM也會退出;3) JVM在垃圾收集時會將失去引用的對象包裝成Finalizer 對象(Reference 的實現),並放入ReferenceQueue ,由Finalizer 線程來處理;最後將該Finalizer對象的引用置爲null,由垃圾收集器來回收;4) JVM爲何要單獨用一個線程來執行finalize() 方法呢?若是JVM的垃圾收集線程本身來作,頗有可能因爲在finalize()方法中誤操做致使GC線程中止或不可控,這對GC線程來講是一種災難; |
Low Memory Detector | JVM | 這個線程是負責對可以使用內存進行檢測,若是發現可用內存低,分配新的內存空間。 |
Reference Handler | JVM | JVM在建立main 線程後就建立Reference Handler 線程,其優先級最高,爲10,它主要用於處理引用對象自己(軟引用、弱引用、虛引用)的垃圾回收問題 。 |
VM Thread | JVM | 這個線程就比較牛b了,是JVM裏面的線程母體,根據hotspot源碼(vmThread.hpp )裏面的註釋,它是一個單個的對象(最原始的線程)會產生或觸發全部其餘的線程,這個單個的VM線程是會被其餘線程所使用來作一些VM操做(如:清掃垃圾等)。 |