每篇一句
但行好事,莫問前程
前言
斷點調試
對IT從業者不是一個陌生的概念,我認爲它是每個
程序猿(媛)從業者都避不開的
且非常重要的技能之一。
那它到底有多重要呢?爲了體現本文的重要性,我引用幾個大佬的話來表述斷點調試
的重要性:
神祕大佬A
:調試技巧比編碼技巧更爲重要,因爲花費在調試上的時間往往比編碼還多,學到的東西比編碼中學到的更豐富神祕大佬B
:調試技能重要性甚⾄超過學習⼀門語⾔神祕大佬C
:不會調試的程序員,肯定編制不出任何好的軟件
大佬都這麼認爲了,so我們需要有一個共識:調試能力是一個程序員最最最基礎的技能。本文主要發力講述調試的相關技能、技巧,希望對你的職業生涯能有所幫助。
說明:本文講解是基於IntelliJ IDEA
而非eclipse
,因此我們從它的斷點對話框開始:
彈出此對話框默認快捷鍵是:Ctrl + Shift +F8
,在這裏你可以管理
你所有的斷點(增刪改)。
鼠標方式可以這樣開啓:Action-view breakpoints / 菜單run-view breakpoints
斷點的基本概念
斷點你可能天天都在使用,但是若真要你對它下定義,估計一時間還有點懵逼呢有木有?
斷點
:是一種附加在源代碼上面的特殊標記,在調試模式(debug模式)
下可以觸發特定的動作
,比如打印線程調用棧信息、計算值、打印指定表達式的值等等。Tips:斷點一但設置就會一直保存在工程中直到手動刪除~
斷點若想生效,必須是調試模式(debug模式)下才行~
斷點參數(斷點屬性)
斷點並不是僅僅是孤立的存在的,它也可以通過參數
進行定製化,這些叫斷點參數
。
不同類型的斷點支持的斷點參數也不盡相同,在下面具體介紹時會詳細說明~
開胃小菜:比如最常用的
條件斷點
,它就是斷點參數的一個典型應用
斷點的種類
據我粗略調查,80%
的小夥伴打斷點只會採用代碼行左邊鼠標單擊這種最基礎的方式打斷點然後調試。其實在現實場景中,有非常非常多的情況下,這種方式將很難快速定位
到問題所在,因此瞭解斷點分類、調試技巧就顯得有點必須了~
殊不知,IDEA
給我們提供了豐富的斷點類型,讓我們能夠在不同的調試場景下,使用不同的斷點類型來大大提高我們的調試效率,畢竟效率就是時間
,而時間就是生命
。
從idea斷點對話框裏也能夠看出斷點是分類的。然後下面我對斷點的分類講解不是完全按此分類,我的分類會更加詳細如下:
Line breakpoint(行斷點)
:在指定代碼行設置斷點,屬於行級別的斷點Temporary line breakpoint(臨時行斷點)
:與行斷點類似,不同之處在於該類型的斷點在被激活之後會被立即刪除Field watchpoint(屬性斷點)
:讀取
或者修改
屬性時會激活屬性斷點Method breakpoint(方法斷點)
:它是標記在方法那一行的斷點,有自己特有的屬性參數Exception breakpoint(異常斷點)
:當程序拋出指定異常時會激活異常斷點。與行斷點不同,異常斷點不需要與源代碼映射(不需要打在具體某一行代碼上),因爲異常斷點應用程序級別的
Line breakpoint(行斷點)
這是使用得最爲廣泛的一種斷點。示例操作“視頻“:
斷點參數
作爲第一個介紹的斷點類型,這裏有必要全面的解釋一下上面行斷點操作的斷點參數:
Suspend
:有沒有讓你詫異到,它竟然是個複選框並且還可以不被選中。若它不被選中的話斷點的相關動作依然激活執行,只是線程不會被組塞了而已。它的兩種阻塞策略如下:
-All
:阻塞該程序內所有線程(默認)
-Thread
:只阻塞當前斷點所在線程(在多線程調試、遠程調試中強烈建議使用這種方式)Condition
:這就是所謂的條件斷點,只有書寫的表達式返回true時候斷點纔會被激活Log
:
- 勾選"Breakpoint hit message":斷點激活時輸出提示日誌
- 勾選"Stack trace":斷點激活時輸出程序調用棧信息
- 勾選"Evaluate and log":並在下面的輸入框中輸入"args",斷點激活時會計算並輸出變量 args 的值
-他哥三是可以同時被勾選的(因爲都是複選框~)
這裏其實已經把絕大多數共用的斷點參數都講述了,這樣下面就會稍微簡單點了~~
Temporary line breakpoint(臨時行斷點)
創建方法不說了,同上。和上面的唯一區別是:把Remove once hit
這個複選框給勾選上(此類型斷點其實使用較少)。
Field watchpoint(屬性斷點)
創建的方式和上無差異。
斷點參數
由於絕大多數參數第一個已經講述了,so這裏只剩一個它獨有的參數:
Watch
:選中"Filed Access" 讀取的時候都會斷住。選中"Filed madification"表示修改的時候都會斷住
Method breakpoint(方法斷點)
打斷點方式同上,只是它是必須把斷點打在方法那一行上。
它也有一個自己獨有的參數:
參數
Watch
:
- “Method entry”:進入方法時激活斷點
- “Method exit”:出去方法時激活斷點
- “Emulated”:目前發現沒啥卵用(求小夥伴不要噴我~)
Exception breakpoint(異常斷點)
異常斷點屬於非常特殊的一種斷點類型,它不對應任何一行代碼
,因爲它屬於程序級別的斷點。
它不能像上面在代碼處直接創建,只能通過上面的斷點對話框來創建。
此處注意:異常斷點中很多選項就是不能使用的(灰色)如下圖示:
斷點參數
但是同理,它也提供一個特有的斷點參數:
Notification
:
- “Catch excetion”:程序在捕獲(Try Catch)這個異常時激活斷點
- “Uncatch excetion”:不catch捕獲異常時激活斷點
小細節:對於不同類型的斷點,打樁後我們看到的圖標也是有差異的,如圖:
關於IDEA
的遠程調試(遠程Debug)
遠程調試
是調試分佈式系統的一個利器。因爲現在都以微服務部署,你不可能在本地同時啓動N個服務來做本地調試。
更重要的是如果測試時候測出發現你的bug,這時候你若想定位問題,通過遠程調試
直接連接到測試服務(甚至是線上服務
)不失爲一種最爲高效的解決方案,並且它還能有非常好的保護現場的輔助能力~
啓動遠程調試主要分兩步:
- 第一步:要讓遠程服務器運行的代碼支持遠程調試,也就是啓動的時候必須加上特定的
JVM參數
:
1.java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${debug_port} demo.jar
(適用於JDK8以上)
2.java -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=${debug_port} demo.jar
(適用於JDK8以下) - 第二步:idea使用remote鏈接遠程端口(注意
ip:port
要對應上):”Edit Configurations” -> “Remote” 配置好後debug啓動
~~~
當你看到控制檯這樣的字樣,就證明你鏈接成功了,進而你可以像調試本地代碼一樣隨意的打各種類型的斷點進行調試了~
需要注意的是:遠程調試時請確保你本地的代碼和遠程的一模一樣。
Java平臺調試體系架構(JPDA)和JDWP(Java Debug Wire Protocol):參考權威文檔 JPDA 體系概覽
以及 JDWP 遠程命令執行漏洞
JDWP
協議是個標準協議,我們的Tomcat是支持的,其實tomcat它的catalina.sh文件裏有告訴你怎麼開啓這個端口:
對上面變量的解析代碼如下(注意有些默認值):
傳統Tomcat怎麼開啓呢?
找到入口文件startup.sh
,最後一句改爲的start前面加上 jpda
如下:
exec "$PRGDIR"/"$EXECUTABLE" jpda start "[email protected]"
或者在catalina.sh中進行配置:
JPDA_OPTS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005’
這樣根據上面的腳本可知,判斷第一個參數等於jpda,所以調試端口開放了。
嵌入式Tomcat怎麼開啓呢?(重要)
這裏主要指的SpringBoot
環境下如何開啓呢?答案見上~
如果想深入瞭解Java調試,強烈給你推薦去這裏看看:深入Java調試體系
總結
本文並沒有介紹所有的斷點參數,如對話框的右半部分的Catch class filter
等等各種filter的參數,因爲我覺得沒太大的用~~~有興趣的自行研究哈
另外本文講述使用的IDE是IntelliJ IDEA
,使用Eclipse的用戶可以仿照着執行~
使用IDEA通過ssh隧道調試遠程服務器代碼_inrgihc的博客-CSDN博客
一、背景
在日常系統開發中,常常需要將程序部署到指定內部網絡環境下,維護人員會通過堡壘機(跳板機)來訪問內網的系統。此景下,當系統需要聯機DEBUG時會受堡壘機的影響。不過依靠ssh的隧道功能,可以很方便的避開此問題。
二、實操
假設網絡拓撲環境如下:
1、在服務器A上開啓遠程調試端口
假設我們在服務器A上可通過如下命令開啓程序調試端口:
-
java -Xms2457m -Xmx2457m -Xmn921m -server \
-
-Dsun.net.inetaddr.ttl=60 \
-
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=29776 \
-
-jar /usr/lib/demo/demo-server-1.0.0-SNAPSHOT.jar
2、在堡壘機X上通過ssh在本地開啓29776端口並映射到服務器A的29776端口上
在堡壘機器X上使用如下命令開啓本地到服務器A的29776端口映射:
ssh -fCNg -L 29776:172.26.5.40:29776 [email protected]
對於ssh各個參數的說明大致如下:
-L <local-port>:<remote-host>:<remote-port> : 本地端口與遠程主機端口的映射配置
-f : 請求 ssh 在執行命令之前轉到後臺
-C : 請求壓縮所有數據(包括 stdin、stdout、stderr 和用於轉發的 X11、TCP 和 UNIX 域連接的數據)
-N : 不執行遠程命令。此選項用於只需要端口轉發功能時
-g: 允許遠程主機連接到本地轉發端口。如果用於多路複用連接,則必須在主進程上指定此選項
詳細參數可參考:利用SSH隧道技術穿越內網訪問遠程設備
3、使用IDEA啓動到堡壘機的DEBUG操作
(1)點擊 + 號找到 Remote JVM Debug
(2) 添加一個Remote JVM Debug
(3)配置堡壘機X的IP地址和做了映射的29776端口;
至此,就完成了遠程DEBUG的相關配置,打上斷點,啓動debug程序吧。




@Test
public void TestTrace() {
Stream.of("beijing","tianjin","shanghai","wuhan")
.map(String::length)
.filter(e->e>5)
.collect(Collectors.toList());
}
