小時候,父母總是叮囑我們玩了玩具之后要收好。如果您仔細想想,其實這種嘮叨并不過分,要保持整潔是因為存在實際的限制,房間里沒有太多的空間,如果到處堆滿了玩具,那么連走路都無處下腳了。
如果有了足夠的空間,保持整潔就不是那么必要了??臻g越多,就越不必要保持整潔。Arlo Guthrie 著名的民謠 Alice's Restaurant Massacre 說明了這一點:
他們住在教堂樓下的大廳,里面的椅子全都搬走了,剩下一個空蕩蕩的大房間,所以他們想,很長時間都不用把垃圾扔出去,有的是地方裝垃圾……
無論如何,垃圾收集可以幫我們減輕內務整理方面的工作。
Java 程序中使用的絕大多數資源都是對象,垃圾收集在清理對象方面做得很好。因此,您可以使用任意多的 String
。垃圾收集器最終無需您的干預就會算出它們何時失效,并收回它們使用的內存。
另一方面,像文件句柄和套接字句柄這類非內存資源必須由程序顯式地釋放,比如使用 close()
、destroy()
、shutdown()
或 release()
這樣的方法來釋放。有些類,比如平臺類庫中的文件句柄流實現,提供終結器(finalizer)作為安全保證,以便當垃圾收集器確定程序不再使用資源而程序卻忘了釋放資源時,終結器還可以來做這個釋放工作。但是盡管文件句柄提供了終結器來在您忘記了時為您釋放資源,最好還是在使用完之后顯式地釋放資源。這樣做可以更早地釋放資源,降低了資源耗盡的可能。
對于有些資源來說,一直等到終結(finalization)釋放它們是不可取的。對于重要的資源,比如鎖獲取和信號量許可證,Lock
或 Semaphore
直到很晚都可能不會被垃圾收集掉。對于數據庫連接這樣的資源,如果您等待終結,那么肯定會消耗完資源。許多數據庫服務器根據許可的容量,只接受一定數量的連接。如果服務器應用程序為每個請求都打開一個新的數據庫連接,然后用完之后就不管了,那么數據庫遠遠未到終結器關閉不再需要的連接,就會到達它的最高容量。
多數資源都不會持續整個應用程序的生命周期,相反,它們只被用于一個活動的生命周期。當應用程序打開一個文件句柄讀取文件以處理文檔時,它通常讀取文件后就不再需要文件句柄了。
在最簡單的情況下,資源在同一個方法調用中被獲取、使用和釋放,比如清單 1 中的 loadPropertiesBadly()
方法:
清單 1. 不正確地在一個方法中獲取、使用和釋放資源 —— 不要這樣做
public static Properties loadPropertiesBadly(String fileName) throws IOException { FileInputStream stream = new FileInputStream(fileName); Properties props = new Properties(); props.load(stream); stream.close(); return props; } |
不幸的是,這個例子存在潛在的資源泄漏。如果一切進展順利,流將會在方法返回之前被關閉。但是如果 props.load()
方法拋出一個 IOException
,那么流則不會被關閉(直到垃圾收集器運行其終結器)。解決方案是使用 try...finally 機制來確保流被關閉,而不管是否發生錯誤,如清單 2 所示: