標籤:ring div apt-get rem row jvm 通知 tar lan
最近,線上一個應用,發現socket數緩慢增長,並且不回收,超過警告線之後,被營運監控自動重啟了。
首先到zabbix上觀察JVM記錄,發現JVM-Perm space最近兩周沒有資料,猜測是程式從JDK7切換到JDK8了。問過開發人員之後,程式已經很久沒有重啟了,最近才重新發布的。而在這期間,線上的Java運行環境已經從JDK7升級到JDK8了。
因為jdk8裡沒有Perm space了,換成了Metaspace。
###netstat
到線上伺服器上,用netstat來統計進程的connection數量。
1 |
netstat -antp | grep pid | wc -l |
發現比zabbix上的統計socket數量要少100多,netstat統計只有100多,而zabbix上監控資料有300多。
於是到/proc/$pid/fd下統計socket類型的fd數量:
12 |
cd /proc/$pid/fdls -al | grep socket | wc -l |
探索資料和zabbix上的資料一致。
###netstat是怎麼統計的
####下載netstat的原始碼
http://unix.stackexchange.com/questions/21503/source-code-of-netstat
1 |
apt-get source net-tools |
從netstat的代碼裡,大概可以看到是讀取/proc/net/tcp裡面的資料來擷取統計資訊的。
####java和c版的簡單netstat的實現
java版的
http://www.cs.earlham.edu/~jeremiah/LinuxSocket.java
C版的:
http://www.netmite.com/android/mydroid/system/core/toolbox/netstat.c
####用starce跟蹤netstat
可以發現netstat把/proc 下的很多資料都讀取出來了。於是大致可以知道netstat是把/proc/pid/fd 下面的資料和/proc/net/下面的資料匯總,對照得到統計結果的。
####哪些socket會沒有被netstat統計到?
又在網上找了下,發現這裡有說到socket如果建立了,沒有bind或者connect,就不會被netstat統計到。
http://serverfault.com/questions/153983/sockets-found-by-lsof-but-not-by-netstat
實際上,也就是如果socket建立了,沒有被使用,那麼就只會在/proc/pid/fd下面有,而不會在/proc/net/下面有相關資料。
簡單測試了下,的確是這樣:
1 |
int socket = socket(PF_INET,SOCK_STREAM,0); //不使用 |
另外,即使socket是使用過的,如果執行shutdown後,剛開始裡,用netstat可以統計到socket的狀態是FIN_WAIT1。過一段時間,netstat統計不到socket的資訊的,但是在/proc/pid/fd下,還是可以找到。
中間的時候,自己寫了個程式,把/proc/pid/fd 下的inode和/proc/net/下面的資料比較,發現的確有些socket的inode不會出現在/proc/net/下。
####用lsof查看
用lsof查看socket inode:
###觸發GC,回收socket
於是嘗試觸發GC,看下socket會不會被回收:
結果,發現socket都被回收了。
再看下AbstractPlainSocketImpl的finalize方法:
123456 |
/** * Cleans up if the user forgets to close it. */protected void finalize() throws IOException { close();} |
可以看到socket是會在GC時,被close掉的。
寫個程式來測試下:
123456789 |
public class TestServer {public static void main(String[] args) throws IOException, InterruptedException {for(int i = 0; i < 10; ++i){ServerSocket socket = new ServerSocket(i + 10000);System.err.println(socket);}System.in.read();}} |
先執行,查看/proc/pid/fd,可以發現有相關的socket fd,再觸發GC,可以發現socket被回收掉了。
##其它的東東
####anon_inode:[eventpoll]
可以看到有像這樣的輸出:
1 |
661 -> anon_inode:[eventpoll] |
這種類型的inode,是epoll建立的。
再扯遠一點,linux下java裡的selector實現是epoll結合一個pipe來實現事件通知功能的。所以在NIO程式裡,會有anon_inode:[eventpoll]和pipe類型的fd。
####為什麼tail -f /proc/$pid/fd/1 不能讀取到stdout的資料
http://unix.stackexchange.com/questions/152773/why-cant-i-tail-f-proc-pid-fd-1
##總結
原因是jdk升級之後,GC的工作方式有變化,FullGC執行的時間變長了,導致有些閒置socket沒有被回收。
本文比較亂,記錄下一些工具和技巧。
netstat統計的tcp串連數與?proc?pid?fd下socket類型fd數量不一致的分析