Java 鉤子程序

簡介

觸發(fā)的時(shí)機(jī)有:

  1. 當(dāng)所有的非deamon線程(守護(hù)線程)結(jié)束, 或者調(diào)用了Systrem.exit()方法 而導(dǎo)致的程序正常的退出
  2. JVM收到需要關(guān)閉自己的信號(比如SIGINT、SIGTERM等烤咧,但像SIGKILL,JVM就沒有機(jī)會(huì)去處理了)琼娘,也或者發(fā)生如系統(tǒng)關(guān)閉這種不可阻擋的事件锰提。

對于addShutdownHook中的鉤子代碼嫌变,也是有一些要注意的地方璃饱,下面列舉幾點(diǎn):

  1. 關(guān)閉鉤子可以注冊多個(gè)胖笛,在關(guān)閉JVM時(shí)就會(huì)起多個(gè)線程來運(yùn)行鉤子眠副。通常來說画切,一個(gè)鉤子就足夠了,但如果需要啟用多個(gè)鉤子囱怕,就需要注意并發(fā)帶來的問題霍弹。
  2. 鉤子里也要注意對異常的處理,如果不幸拋出了異常娃弓,那么鉤子的執(zhí)行序列就會(huì)被終止典格。
  3. 在鉤子運(yùn)行期間,工作線程也在運(yùn)行台丛,需要考慮到工作線程是否會(huì)對鉤子的執(zhí)行帶來影響
  4. 鉤子里的代碼盡可能簡潔耍缴,否則當(dāng)像系統(tǒng)關(guān)閉等情景可能鉤子來不及運(yùn)行完JVM就被退出了。

信號觸發(fā)

使信號觸發(fā)JVM的鉤子程序

public class HookTest {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Hook());
        while(true){}
    }

    static class Hook extends Thread{

        @Override
        public void run() {
            System.out.println("Hook execute!!!");
        }
    }
}

運(yùn)行鉤子程序

nohup java HookTest &

關(guān)閉程序

kill HookTest_PID

我們可以在nohup程序中看到Hook execute!!!輸出

我從JVMs and kill signals看到一篇博客, 這個(gè)上面總結(jié)了哪些信號會(huì)導(dǎo)致JVM運(yùn)行Hook

signal          shutdown    runs hook   exit code   comment
default (15)    yes         yes         143         SIGTERM is the default unix kill signal
0               no          -           -   
1 (SIGHUP)      yes         yes         129 
2 (SIGINT)      yes         yes         130         SIGINT is the signal sent on ^C
3 (SIGQUIT)     no          -           -           觸發(fā) JVM dump threads / stack-traces
4 (SIGILL)      yes         no          134         觸發(fā) JVM 輸出一個(gè) core dump 文件, 同時(shí)abort on trap 6
5               yes         no          133         Makes the JVM exit with "Trace/BPT trap: 5"
6 (SIGABRT)     yes         no          134         Makes the JVM exit with "Abort trap: 6"
7               yes         no          135         Makes the JVM exit with "EMT trap: 7"
8 (SIGFPE)      yes         no          134         Makes the JVM write a core dump and abort on trap 6
9 (SIGKILL)     yes         no          137         The JVM is forcibly killed (exits with "Killed: 9")
10 (SIGBUS)     yes         no          134         Emulates a "Bus Error"
11 (SIGSEGV)    yes         no          134         Emulates a "Segmentation fault"
12              yes         no          140         Makes the JVM exit with "Bad system call: 12"
13              no          -           -           
14              yes         no          142         Makes the JVM exit with "Alarm clock: 14"
15 (SIGTERM)    yes         yes         143         This is the default unix kill signal
16              no          -           -           
17              no          -           145         Stops the application (sends it to the background), same as ^Z
18              no          -           146         Stops the application (sends it to the background), same as ^Z
19              no          -           -           
20              no          -           -           
21              no          -           149         Stops the application (sends it to the background), same as ^Z
22              no          -           150         Stops the application (sends it to the background), same as ^Z
23              no          -           -           
24              yes         no          152         Makes the JVM exit with "Cputime limit exceeded: 24"
25              no          -           -           
26              yes         no          154         Makes the JVM exit with "Virtual timer expired: 26"
27              yes         no          155         Makes the JVM exit with "Profiling timer expired: 27"
28              no          -           -           
29              no          -           -           
30              yes         no          158         Makes the JVM exit with "User defined signal 1: 30"
31              yes         no          134         Makes the JVM exit on Segmentation fault

內(nèi)存溢出觸發(fā)

測試JVM棧溢出后調(diào)用鉤子程序

public class HookTest {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Hook());
        exec();
    }

    public static void exec() {
        exec();
    }

    static class Hook extends Thread{

        @Override
        public void run() {
            System.out.println("Hook execute!!!");
        }
    }
}

運(yùn)行后輸出為

D:\testOOM>java HookTest
Exception in thread "main" java.lang.StackOverflowError
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    ...
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
    at HookTest.exec(HookTest.java:9)
Hook execute!!!

D:\testOOM>

為了測試在更加復(fù)雜的環(huán)境下, Hook的使用情況, 看下面的測試代碼

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class HookTest {

    private static Map<String, String> cache = new HashMap<>();

    public static void main(String[] args) {
        cache.put("abc", "abc");

        Runtime.getRuntime().addShutdownHook(new Hook());

        byte[] bytes = new byte[1024 * 1024 *1024 * 1024];
    }

    static class Hook extends Thread{

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(LocalDateTime.now());
                System.out.println("    freeMemory : " + Runtime.getRuntime().freeMemory());
                System.out.println("    maxMemory : " + Runtime.getRuntime().maxMemory());
                System.out.println("    totalMemory : " + Runtime.getRuntime().totalMemory());
                System.out.println("    currentThread name : " + Thread.currentThread().getName());
                System.out.println("    cache size : " + cache.size());
                cache.put(LocalDateTime.now().toString(), LocalDateTime.now().toString());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

運(yùn)行后的輸出結(jié)果為

ζ java HookTest
2016-07-09T16:12:12.479
    freeMemory : 155922512
    maxMemory : 2375024640
    totalMemory : 160956416
    currentThread name : Thread-0
    cache size : 1
2016-07-09T16:12:13.480
    freeMemory : 155922512
    maxMemory : 2375024640
    totalMemory : 160956416
    currentThread name : Thread-0
    cache size : 2
2016-07-09T16:12:14.480
    freeMemory : 155922512
    maxMemory : 2375024640
    totalMemory : 160956416
    currentThread name : Thread-0
    cache size : 3
2016-07-09T16:12:15.480
    freeMemory : 155922512
    maxMemory : 2375024640
    totalMemory : 160956416
    currentThread name : Thread-0
    cache size : 4
...

正常結(jié)束觸發(fā)

測試程序正常結(jié)束后也會(huì)調(diào)用鉤子程序

public class HookTest {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Hook());
    }

    static class Hook extends Thread{

        @Override
        public void run() {
            System.out.println("Hook execute!!!");
        }
    }
}

運(yùn)行結(jié)果為

D:\testOOM>java HookTest
Hook execute!!!

D:\testOOM>

調(diào)用exit()觸發(fā)

public class HookTest {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Hook());
        System.exit(0);

        System.out.println("Main over");
    }

    static class Hook extends Thread{

        @Override
        public void run() {
            System.out.println("Hook execute!!!");
        }
    }
}

運(yùn)行結(jié)果為

D:\testOOM>java HookTest
Hook execute!!!

D:\testOOM>

不被觸發(fā)

再google上找到了一篇這樣的文章Know the JVM Series: Shutdown Hooks里面介紹了鉤子程序在什么情況下不會(huì)執(zhí)行
盡管上面列舉出了N多觸發(fā)鉤子程序的示例, 但是并不保證這個(gè)鉤子程序總是能被觸發(fā)執(zhí)行的, 例如

  • JVM內(nèi)部發(fā)生錯(cuò)誤, 可能還沒有來得及觸發(fā)鉤子程序, JVM就掛掉了(JVM 發(fā)生內(nèi)部錯(cuò)誤, 有沒有日志呢?)
  • 還有上面我們給出的那個(gè)信號表, 如果操作系統(tǒng)發(fā)送出上面的信號的話, 同樣的, JVM沒有執(zhí)行鉤子程序就退出了
  • 還有調(diào)用Runime.halt()函數(shù)也不會(huì)執(zhí)行鉤子程序

還有一種情況是, 當(dāng)操作系統(tǒng)向進(jìn)程發(fā)送一個(gè)SIGTERM信號之后, 如果進(jìn)程沒有在指定的時(shí)間之內(nèi)關(guān)閉, 那么操作系統(tǒng)會(huì)強(qiáng)制將該進(jìn)程殺掉, 如此一來鉤子程序也不會(huì)得到完整的執(zhí)行(因?yàn)殂^子程序可能執(zhí)行到一半就被操作系統(tǒng)殺死了). 因此不管是這篇文章還是JDK API都推薦不要在鉤子程序里寫復(fù)雜的業(yè)務(wù)邏輯, 避免產(chǎn)生死鎖或者產(chǎn)生長時(shí)間的IO操作, 盡可能快地讓鉤子程序執(zhí)行完畢.

在oracle上的Design of the Shutdown Hooks API同樣見到這樣一句話,

Will shutdown hooks be run if the VM crashes?
    If the VM crashes due to an error in native code then no guarantee can be made about whether or not the hooks will be run.

哎,, 怎么著才能監(jiān)控JVM掛掉的信息呢挽霉?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末防嗡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侠坎,更是在濱河造成了極大的恐慌蚁趁,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件实胸,死亡現(xiàn)場離奇詭異他嫡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)庐完,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門涮瞻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人假褪,你說我怎么就攤上這事署咽。” “怎么了生音?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵宁否,是天一觀的道長。 經(jīng)常有香客問我缀遍,道長慕匠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任域醇,我火速辦了婚禮台谊,結(jié)果婚禮上蓉媳,老公的妹妹穿的比我還像新娘。我一直安慰自己锅铅,他們只是感情好酪呻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盐须,像睡著了一般玩荠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贼邓,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天阶冈,我揣著相機(jī)與錄音,去河邊找鬼塑径。 笑死女坑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的统舀。 我是一名探鬼主播堂飞,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绑咱!你這毒婦竟也來了绰筛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤描融,失蹤者是張志新(化名)和其女友劉穎铝噩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窿克,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骏庸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了年叮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片具被。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖只损,靈堂內(nèi)的尸體忽然破棺而出一姿,到底是詐尸還是另有隱情,我是刑警寧澤跃惫,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布叮叹,位于F島的核電站,受9級特大地震影響爆存,放射性物質(zhì)發(fā)生泄漏蛉顽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一先较、第九天 我趴在偏房一處隱蔽的房頂上張望携冤。 院中可真熱鬧悼粮,春花似錦、人聲如沸曾棕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睁蕾。三九已至,卻和暖如春债朵,著一層夾襖步出監(jiān)牢的瞬間子眶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工序芦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臭杰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓谚中,卻偏偏與公主長得像渴杆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子宪塔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理磁奖,服務(wù)發(fā)現(xiàn),斷路器某筐,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 一.線程安全性 線程安全是建立在對于對象狀態(tài)訪問操作進(jìn)行管理比搭,特別是對共享的與可變的狀態(tài)的訪問 解釋下上面的話: ...
    黃大大吃不胖閱讀 842評論 0 3
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司南誊,掛了不少身诺,但最終還是拿到小米、百度抄囚、阿里霉赡、京東、新浪幔托、CVTE穴亏、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評論 11 349
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法重挑,內(nèi)部類的語法迫肖,繼承相關(guān)的語法,異常的語法攒驰,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 通常很多人會(huì)把瑜伽當(dāng)做健身或運(yùn)動(dòng)的一種蟆湖,甚至在一些詞條分類中,瑜伽也是屬于健身的項(xiàng)目' 這個(gè)誤會(huì)可能已經(jīng)很久了……...
    訪問權(quán)com閱讀 872評論 0 3