在項目上遇到一個系統會突然down掉的問題,因為並沒有詳細的日誌資訊,百思不得其解,終於有一天這個問題再次出現,捕獲的日誌資訊為:
ERROR: transport error 202: handshake failed - connection prematurally closed ["transport.c",L41]
JDWP exit error JVMTI_ERROR_NONE(0): could not connect, timeout or fatal error
在網上一查,真相大白,原來是jkd1.5的一個關於debug運行方式的一個bug,原因是由於接收到不符合JDWP協議的資料包,導致JVM崩潰. 問題原因:
該故障是JVM遠程debug存在的缺陷,只有在開啟遠程debug連接埠時才會出現;原因是由於接收到不符合JDWP協議的資料包,導致JVM崩潰。
要確認系統中是否存在該漏洞,可以檢查java啟動參數中是否有如下相關配置:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9001
或者-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9001
若存在相關配置,那就說明java啟動了遠端偵錯連接埠,就會存在該漏洞;此時JVM虛擬機器作為調試的服務提供端,通過8787連接埠監聽一個串連,而調試器通過該串連與虛擬機器進行互動。
從官方文檔中摘錄幾個配置案例:
-Xrunjdwp:transport=dt_socket,server=y,address=8000
在8000連接埠監聽Socket串連,掛起VM(suspend預設為y)並且不載入運行主函數直到調試請求到達
-Xrunjdwp:transport=dt_shmem,server=y,suspend=n
選擇一個可用的共用記憶體(因為沒有指定address)並監聽該記憶體串連,同時載入運行主函數,不掛起VM
-Xrunjdwp:transport=dt_socket,address=myhost:8000
串連到myhost:8000提供的調試服務(server=n,以調試用戶端存在),掛起VM並且不載入運行主函數
-Xrunjdwp:transport=dt_shmem,address=mysharedmemory
通過共用記憶體的方式串連到調試服務,掛起VM並且不載入運行主函數
-Xrunjdwp:transport=dt_socket,server=y,address=8000,onthrow=java.io.IOException,launch=/usr/local/bin/debugstub
等待java.io.IOException被拋出,然後掛起VM並監聽8000連接埠串連,在接到調試請求後以命令/usr/local/bin/debugstub dt_socket myhost:8000執行
-Xrunjdwp:transport=dt_shmem,server=y,onuncaught=y,launch=d:/bin/debugstub.exe
等待一個RuntimeException被拋出,然後掛起VM並監聽一個可用的共用記憶體,在接到調試請求後以命令d:/bin/debugstub.exe dt_shmem <address>執行,<address>是可用的共用記憶體
問題解決:
一般線上啟動並執行應用都不會開啟遠程Debug模式,各應用伺服器的預設配置也是不開啟的,就算開啟了該debug連接埠也會有防火牆保護。
解決該問題的方法有兩種,任選其一即可:
1、在JDK1.5環境下關閉遠端偵錯模式,但線上環境的部署指令碼一定要仔細review才行;
2、升級JDK到1.6b49以上版本,這就要看應用的相容性了,一般來說不會有什麼問題;
3、笨方法:不在使用這個方法,哈哈:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9001
4、或者使用如下配置臨時代替:-Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false