系統配置爲單核4G, web 工程配置堆2G, /tmp目錄 二進制文件不斷增長,平均一天增長20G, 手動清理/tmp目錄,重啓系統,問題依舊。java
/tmp 目錄存放系統運行時產生的臨時文件。在Redhat-like系統上,會按期清理/tmp目錄下10天未訪問的文件。這個機制保證了,linux不會像windows那樣在較長時間運行後變得臃腫不堪。mysql
清理腳本位於/etc/cron.daily/tmpwatch,內容以下,linux
#! /bin/shnginx
flags=-umcweb
/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \sql
-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \apache
-X '/tmp/hsperfdata_*' 10d /tmpwindows
/usr/sbin/tmpwatch "$flags" 30d /var/tmpapi
for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do緩存
if [ -d "$d" ]; then
/usr/sbin/tmpwatch "$flags" -f 30d "$d"
fi
done
本質是調用了系統命令/usr/sbin/tmpwatch 來執行對/tmp和/var/tmp目錄的清理。tmpwatch 通常被用來清掃那些用來臨時駐留文件的目錄。
注意到,tmpwatch刪除時有個排除項,/tmp/hsperfdata_*,java程序在啓動時,默認會生成/tmp/hsperfdata_{USERNAME}/{PID}的文件,
這個文件會存儲jvm運行的相關信息。jps\jconsole等工具的數據源就是來自於這個文件。若這個文件不存在,jps命令執行時找不到這個進程。關於這個問題,曾經有個bug相關(https://bugzilla.redhat.com/show_bug.cgi?id=527425),這個bug就是因爲tmpwatch 沒有排除/tmp/hsperfdata_*這個目錄,致使該目錄被刪除,jps沒法找到對應的進程。
那麼/tmp一般會有哪些臨時數據在這裏呢?
例如,jvm啓動數據、mysql的sock文件、apache默認的上傳文件目錄、nginx的緩存文件以及一些其餘進程的臨時文件。
1.查看/tmp, 經過 ls /tmp | wc -l觀察, /tmp文件大約以4個/s 的速度增長,並且都是二進制文件。
2.lsof -p pid 肯定tmp文件都被進程id爲10791的同一個Java進程打開。
根據上面的分析,這些文件應該是該進程的臨時文件,並且不斷在增長,有多是文件句柄泄露。
查看該進程的句柄圖
12.15號,系統打開的句柄數量在逐步的增長,並且沒有出現相對平穩的跡象,肯定是句柄泄露了,這印證了咱們的猜測。
下來須要進一步分析到底是什麼緣由形成的句柄泄露。
查看文件內容,vi -b /tmp/filename, 包含 <</Length 2541/Filter/FlateDecode>>stream 之類的內容。猜想應該是和解碼有關。
谷歌搜索關鍵字,肯定FlateDecode是解碼 PDF stream 的一個工具。查看程序中引用相關pdf的代碼,以下圖所示:
|
這段代碼用來將pdf轉化成一個jpg的圖片,使用了jpedal第三方庫。
jpedal是一個開源的純Java的PDF文檔解析庫,能夠用來方便的查看和編輯文字和圖片。
回到代碼, 按照以往編碼的經驗,有多是PdfDecoder沒有釋放資源,致使生成的臨時文件一直沒有釋放掉。查看jpedal文檔,發現的確提供了closePdfFile 關閉pdf文件的方法。
finally 塊裏添加
decode_pdf.flushObjectValues(true);
decode_pdf.closePdfFile();
從新發版,發現以後句柄圖達到了相對平穩的狀態,tmp目錄也再也不繼續增長臨時文件。
雖然問題解決了,可是還有一些困惑。臨時文件怎麼生成的?page fault爲啥這麼多?
一、臨時文件究竟是怎麼生成的?
decode_pdf.openPdfArray(bytes) 根據傳進來的字節流 打開pdf文件,
jpedal在這裏作了一個優化,當pdf文件小於16k時或者alwaysCacheInMemory = -1時,直接內存緩存該pdf。
當pdf文件的大小大於16k時,會在臨時目錄下生成一個前綴爲page,後綴爲bin的二進制文件,該臨時目錄由系統參數 java.io.tmpdir 指定,默認在/tmp目錄下。
這樣,因爲線上環境的pdf基本都大於16k,因此/tmp目錄下就會看到不斷的臨時文件生成。這個臨時文件命名規則爲page***.bin。
二、添加closePdf文件以後,爲啥問題就解決了呢?
closePdf會調用PdfReader的closePdfFile()方法,該方法根據緩存的臨時文件名稱刪除該臨時文件。
三、未關閉pdf文件,爲啥會引發較多的page fault呢?
page fault 分爲 minor page fault 和major page fault。
major page fault也稱爲hard page fault, 指須要訪問的內存不在虛擬地址空間,也不在物理內存中,須要從慢速設備載入。從swap回到物理內存也是hard page fault。
minor page fault也稱爲soft page fault, 指須要訪問的內存不在虛擬地址空間,可是在物理內存中,只須要MMU創建物理內存和虛擬地址空間的映射關係便可。
(一般是多個進程訪問同一個共享內存中的數據,可能某些進程尚未創建起映射關係,因此訪問時會出現soft page fault)
正常狀況下,系統也會有一些pagefault,以下圖所示:
,因此pagefault和該問題沒有直接關係。minflt表示從內存加載數據時每秒出現的小的錯誤數目,能夠忽略。若是majflt較大,表示從磁盤載入內存頁面,發生了swap,此時須要關注。
咱們詳細的回顧了這次線上發生的問題,以及如何去定位,而後去解決問題的整個過程。
(1)問題發現,收到系統磁盤空間不足的報警。
(2)問題定位,先根據du確認是tmp目錄增加過快的問題,而後根據lsof和進程句柄圖肯定是文件句柄泄露,再根據臨時文件的文件內容,定位相關的源代碼,查看源代碼,確認是文件句柄資源沒有正確釋放。
(3)解決問題,查看api,確認是資源泄露的問題,修復代碼上線。