国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

理論與實踐: 用弱引用堵住內存泄漏

2019-11-18 12:14:32
字體:
來源:轉載
供稿:網友

  雖然用 java? 語言編寫的程序在理論上是不會出現“內存泄漏”的,但是有時對象在不再作為程序的邏輯狀態(tài)的一部分之后仍然不被垃圾收集。本月,負責保障應用程序健康的工程師 Brian Goetz 探討了無意識的對象保留的常見原因,并展示了如何用弱引用堵住泄漏。

  要讓垃圾收集(GC)回收程序不再使用的對象,對象的邏輯 生命周期(應用程序使用它的時間)和對該對象擁有的引用的實際 生命周期必須是相同的。在大多數時候,好的軟件工程技術保證這是自動實現的,不用我們對對象生命周期問題花費過多心思。但是偶然我們會創(chuàng)建一個引用,它在內存中包含對象的時間比我們預期的要長得多,這種情況稱為無意識的對象保留(unintentional object retention)。

  全局 Map 造成的內存泄漏

  無意識對象保留最常見的原因是使用 Map 將元數據與臨時對象(transient object)相關聯。假定一個對象具有中等生命周期,比分配它的那個方法調用的生命周期長,但是比應用程序的生命周期短,如客戶機的套接字連接。需要將一些元數據與這個套接字關聯,如生成連接的用戶的標識。在創(chuàng)建 Socket 時是不知道這些信息的,并且不能將數據添加到 Socket 對象上,因為不能控制 Socket 類或者它的子類。這時,典型的方法就是在一個全局 Map 中存儲這些信息,如清單 1 中的 SocketManager 類所示:

  清單 1. 使用一個全局 Map 將元數據關聯到一個對象

public class SocketManager {
    PRivate Map<Socket,User> m = new HashMap<Socket,User>();

    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
    public void removeUser(Socket s) {
        m.remove(s);
    }
}

SocketManager socketManager;
...
socketManager.setUser(socket, user);

  這種方法的問題是元數據的生命周期需要與套接字的生命周期掛鉤,但是除非準確地知道什么時候程序不再需要這個套接字,并記住從 Map 中刪除相應的映射,否則,Socket 和 User 對象將會永遠留在 Map 中,遠遠超過響應了請求和關閉套接字的時間。這會阻止 Socket 和 User 對象被垃圾收集,即使應用程序不會再使用它們。這些對象留下來不受控制,很輕易造成程序在長時間運行后內存爆滿。除了最簡單的情況,在幾乎所有情況下找出什么時候 Socket 不再被程序使用是一件很煩人和輕易出錯的任務,需要人工對內存進行治理。

  找出內存泄漏

  程序有內存泄漏的第一個跡象通常是它拋出一個 OutOfMemoryError,或者因為頻繁的垃圾收集而表現出糟糕的性能。幸運的是,垃圾收集可以提供能夠用來診斷內存泄漏的大量信息。假如以 -verbose:gc 或者 -Xloggc 選項調用 JVM,那么每次 GC 運行時在控制臺上或者日志文件中會打印出一個診斷信息,包括它所花費的時間、當前堆使用情況以及恢復了多少內存。記錄 GC 使用情況并不具有干擾性,因此假如需要分析內存問題或者調優(yōu)垃圾收集器,在生產環(huán)境中默認啟用 GC 日志是值得的。

  有工具可以利用 GC 日志輸出并以圖形方式將它顯示出來,JTune 就是這樣的一種工具(請參閱 參考資料)。觀察 GC 之后堆大小的圖,可以看到程序內存使用的趨勢。對于大多數程序來說,可以將內存使用分為兩部分:baseline 使用和 current load 使用。對于服務器應用程序,baseline 使用就是應用程序在沒有任何負荷、但是已經預備好接受請求時的內存使用,current load 使用是在處理請求過程中使用的、但是在請求處理完成后會釋放的內存。只要負荷大體上是恒定的,應用程序通常會很快達到一個穩(wěn)定的內存使用水平。假如在應用程序已經完成了其初始化并且負荷沒有增加的情況下,內存使用持續(xù)增加,那么程序就可能在處理前面的請求時保留了生成的對象。

  清單 2 展示了一個有內存泄漏的程序。MapLeaker 在線程池中處理任務,并在一個 Map 中記錄每一項任務的狀態(tài)。不幸的是,在任務完成后它不會刪除那一項,因此狀態(tài)項和任務對象(以及它們的內部狀態(tài))會不斷地積累。

  清單 2. 具有基于 Map 的內存泄漏的程序

public class MapLeaker {
    public ExecutorService exec = Executors.newFixedThreadPool(5);
    public Map<Task, TaskStatus> taskStatus
        = Collections.synchronizedMap(new HashMap<Task, TaskStatus>());
    private Random random = new Random();



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 洱源县| 民县| 台安县| 久治县| 双城市| 葵青区| 锦屏县| 丰城市| 宜宾县| 张家口市| 泌阳县| 乌兰察布市| 桓台县| 阜新市| 金沙县| 定日县| 香河县| 三门峡市| 苏州市| 元氏县| 田林县| 清徐县| 安远县| 银川市| 江门市| 盈江县| 云龙县| 芦溪县| 布尔津县| 肥西县| 交城县| 呼伦贝尔市| 湘潭市| 南溪县| 广昌县| 中西区| 阿克陶县| 夏河县| 邳州市| 武鸣县| 泾阳县|