Java IO Stream控制程式碼洩露分析
Java io包封裝了常用的I/O操作,流式操作被統一為InputStream、OutputStream、Reader、Writer四個抽象類,其中InputStream和OutputStream為位元組流,Reader和Writer為字元流。流式抽象的好處在於程式設計師無需關心資料如何傳輸,只需按位元組或字元依次操作即可。
在使用流式物件進行操作時,特別是檔案操作,使用完畢後千萬不能忘記呼叫流物件的close()方法,這也是老生常談的話題,但是偏偏很容易忘記,或者沒忘記但是使用不當導致close()方法沒呼叫到。正確的做法是把close()方法放在finally塊中呼叫,比如這樣:
InputStream in = ...;OutputStream out = ...;try { doSth(in, out);} catch (Exception e) { handleException();} finally { try { in.close(); } catch (Exception e1){ } try { out.close(); } catch (Exception e2){ }}
Java流式操作在便捷了I/O操作的同時,也帶來了錯誤處理上覆雜性,這也是Java被人詬病的理由之一。Golang在這塊的處理就非常優雅,它提供了defer關鍵字來簡化流程。
當檔案流未被顯式關閉時,會產生怎樣的後果?結果就是會引起檔案描述符(fd)耗盡。以下程式碼會開啟一個檔案10次,向系統申請了10個fd。
public static void main(String[] args) throws Exception { for (int i = 0; i到/proc/{pid}/fd目錄下確定已開啟的檔案描述符:
root@classa:/proc/16333/fd# ls -ltotal 0lrwx------ 1 root root 64 Aug 2 20:43 0 -> /dev/pts/3lrwx------ 1 root root 64 Aug 2 20:43 1 -> /dev/pts/3lr-x------ 1 root root 64 Aug 2 20:43 10 -> /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/ext/sunjce_provider.jar lr-x------ 1 root root 64 Aug 2 20:43 11 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 12 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 13 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 14 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 15 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 16 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 17 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 18 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 19 -> /root/file lrwx------ 1 root root 64 Aug 2 20:43 2 -> /dev/pts/3lr-x------ 1 root root 64 Aug 2 20:43 20 -> /root/file lr-x------ 1 root root 64 Aug 2 20:43 3 -> /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar但是根據以往經驗,即使流未顯式關閉,也沒見過檔案描述符耗盡的情況。這是因為Java檔案流式類做了保護措施,FileInputStream和FileOutputStream類利用Java的finalizer機制向GC註冊了資源回收的回撥函式,當GC回收物件時,例項物件的finalize()方法被呼叫。以FileInputStream為例,看看它是怎麼處理的:
/** * Ensures that the close method of this file input stream is * called when there are no more references to it. * * @exception IOException if an I/O error occurs. * @see java.io.FileInputStream#close() */protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { /* * Finalizer should not release the FileDescriptor if another * stream is still using it. If the user directly invokes * close() then the FileDescriptor is also released. */ runningFinalize.set(Boolean.TRUE); try { close(); } finally { runningFinalize.set(Boolean.FALSE); } }}當fd未釋放時,finalize()方法會呼叫close()方法關閉檔案描述符。有了這一層保障後,即使程式設計師粗心忘了關閉流,也能保證流最終會被正常關閉了。以下程式可以驗證:
public static void main(String[] args) throws Exception { for (int i = 0; iJava執行引數加上GC資訊便於觀察:
# java -verbose:gc -XX:+PrintGCDetails -Xloggc:gc.log -XX:+PrintGCTimeStamps StreamTest程式在pause1處開啟了10個fd,接著強制透過System.gc()觸發一次GC,等gc.log中GC日誌輸出後再觀察/proc/{pid}/fd目錄,發現已開啟的檔案描述符均已經關閉。
但是即便如此,依然存在資源洩漏導致程式無法正常工作的情況,因為JVM規範並未對GC何時被喚起作要求,而物件的finalize()只有在其被回收時才觸發一次,因此完全存在以下情況:在兩次GC週期之間,檔案描述符被耗盡!這個問題曾經在生產環境中出現過的,起因是某同事在原本只需載入一次的檔案讀取操作寫成了每次使用者請求載入一次,在一定的併發數下就導致too many open file的異常:
Exception in thread "main" java.io.FileNotFoundException: file (Too many open files) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:146) ......原文連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2157/viewspace-2811121/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資訊洩露之web原始碼洩露Web原始碼
- 記一次奇怪的檔案控制程式碼洩露問題
- Lowmemorykiller記憶體洩露分析記憶體洩露
- ThreadLocal原始碼解讀和記憶體洩露分析thread原始碼記憶體洩露
- Windows Secondary Logon服務中的一個控制程式碼許可權洩露BugWindowsGo
- 用 JS 程式碼解釋 Java StreamJSJava
- 使用 mtrace 分析 “記憶體洩露”記憶體洩露
- ctf常見原始碼洩露原始碼
- Java IO原始碼分析(三)——PipedOutputStream和PipedInputStreamJava原始碼
- dp洩露
- Spring Boot heapdump洩露記憶體分析方法Spring Boot記憶體
- 程式碼簽名證書一旦洩露 危害有多大
- Pprof定位Go程式記憶體洩露Go記憶體洩露
- ch19_IO_stream
- java中如何檢視記憶體洩露Java記憶體洩露
- 三星多個專案程式碼洩露 包括SmartThings原始碼和金鑰原始碼
- 一個 Vue 頁面的記憶體洩露分析Vue記憶體洩露
- 一個Vue頁面的記憶體洩露分析Vue記憶體洩露
- CVE-2016-0059 IE資訊洩露漏洞分析
- 小題大做 | Handler記憶體洩露全面分析記憶體洩露
- [CTFHUB]SVN洩露
- 微軟Chromium版Edge安裝程式洩露微軟
- JAVA記憶體洩露的原因及解決Java記憶體洩露
- 記一次 .NET 某流媒體獨角獸 API 控制程式碼洩漏分析API
- Camera 360應用隱私資料洩露的分析
- 利用dotnet-dump分析docker容器記憶體洩露Docker記憶體洩露
- java IO流 對檔案操作的程式碼集合Java
- 如何用Java與python程式碼解釋IO模型JavaPython模型
- 我們分析了10萬條洩露密碼,發現了這樣的套路密碼
- android IO Prefetch原始碼分析Android原始碼
- 如何防止 goroutine 洩露Go
- 企業如何有效防止原始碼洩露及篡改?原始碼
- 【總結】註冊碼洩露原理以及例題
- C程式記憶體洩露檢測工具——ValgrindC程式記憶體洩露
- 一行程式碼教你解決FlutterPlatformViews記憶體洩露(memory leak)行程FlutterPlatformView記憶體洩露
- 全面吃透JAVA Stream流操作,讓程式碼更加的優雅Java
- 大模型隱私洩露攻擊技巧分析與復現大模型
- 使用Windbg快速分析應用記憶體洩露問題記憶體洩露