成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java程序性能優化之找出內存溢出元兇

開發 后端
本文所講的是關于通過Java程序來找出內存溢出的原因的研究。

我曾經在剛入行的時候做過一個小的swing程序,用到了java SE,swing,Thread等東東,當初經驗少也沒有做過嚴格的性能測試,布到生產環境用了一段時間后發現那個小程序有時候會拋java.lang.OutofMemoryError異常,就是java的內存溢出。當時也上網查了不少資料,試過一些辦法,代碼也稍微做了些優化,但是有一個問題我始終是找不到解決的方案 - 不知為什么子窗體關閉后java的垃圾回收機制無法回收其資源,因為這個Java程序可能要經常開關一些子窗體,那么這些子窗體關閉后無法釋放資源就造成了Java程序OutOfMemoryError的潛在的隱患!

最近無意間在網上看到了一個監控java程序內存使用的工具 - JProbe,馬上回想起那個有關內存溢出的難題,于是我就下載了JProbe8.0.0希望從分析內存入手找到我要的答案。軟件下載安裝后,在安裝目錄里詳盡的用戶指南(懂點軟件和英語的人很快就能上手),主要是兩個步驟:

1.用JProbe Config工具根據提示生成J2SE或者J2EE程序的控制腳本(一個.jpl文件和一個.bat文件),在命令行里進入.bat文件所在的目錄,然后執行該批處理讓要監控的java程序跑起來

2.運行JProbe Console工具,點擊“Attach to Session...”按鈕,彈出java程序的內存實時監控圖表“Runtime Summary”,我們主要是看“Data”卡片里的內容(注意:第一次使用該軟件可能會遇到一些小問題:比如發布為jar包的程序如果運行時會去讀配置文件,從控制腳本啟動的話,可能會發生配置文件找不到的異常,解決辦法是:不要打jar包,直接就用文件夾發布;還有可能因為一些殺毒軟件的網絡防火墻導致JProbe無法連接到控制腳本的session,造成監控圖表打不開,解決辦法是:取消防火墻對于JProbe訪問網絡的限制)

實時監控圖表“Runtime Summary”如下圖所示: 
 

實時監控圖表

可以設置要監視的包或者類,然后點擊“Refresh Runtime Data”按鈕刷新這些對象占用內存的情況,當你覺得某個類比較可疑的話,你可以在不斷的使用程序的過程中監視它的生命周期,看看它是否像預期的那樣在結束了生命周期后占用的內存就被釋放。眾所周知:java的內存回收是自動進行的,無需程序員干預,我們稱其為垃圾回收,這個垃圾回收可能是不定期的,就是當程序占用內存資源比較少的情況下可能jvm的垃圾回收頻率就比較低;反之,java程序消耗內存資源比較多的情況下,垃圾回收的頻率和力度就比較高,這種垃圾回收的不確定性很可能會影響我們的判斷,但我們可以點擊JProbe監控界面右上方的“Request a Garbage Collection”(像一個垃圾桶的圖標)按鈕來向jvm發出垃圾回收的請求,等幾秒后再去點擊“Refresh Runtime Data”,這個時候如果那個預期應該已經銷毀的對象的類名還是沒有從監控界面下方的class列中消失或者其對象數量沒有減少的話(請多試幾次,中間可以夾雜些其他增加程序內存使用的操作以確保jvm確實執行了垃圾回收),那恭喜你!90%的可能性你已經找到了程序的某個缺陷

這個查找元兇的過程可能是相當耗時的,是對程序員的耐心的挑戰。我熬了一下午一晚上,功夫不負有心人,基本上把我那個小程序的所有內存溢出的漏洞都找到并補上了。事實告訴我之前那個子窗體關閉后資源無法釋放的根本原因是:子窗體雖然調用了dispose()方法,但是子窗體對象的引用一直都在:或者是被靜態HashMap引用、或者是它的內部子線程類沒有釋放、或者是它的某個事件監聽類沒有釋放(借用JProbe的火眼金睛一檢驗,發現問題真是一大堆啊!),so.我們要徹底釋放某個對象占用資源的關鍵在于找到并釋放所有對它的引用!

下面是我解決具體問題的一些經驗:

程序中造成內存溢出可能性最大的是HashMap,Hashtable等等集合類,尤其是靜態的,更是要慎之又慎!!!它們引用的對象可能你感覺已經銷毀了,其實很可能你忘記remove鍵值,而如果這些集合對象還是靜態的掛在其他類里面,那么這個引用可能一直都在,借用JProbe測試一下,結果往往出人意料,解決辦法:徹底刪除鍵,remove、clear,如果允許最好把集合對象設為null

對于不再使用的線程對象,如果要徹底殺了它,很多書上都推薦用join方法,我之前也是這樣做的,但后來借助JProbe工具我吃驚的發現這樣做很可能要殺的線程仍舊好好的活在你日益增大的內存里,很可能調用了線程的sleep方法后用join方法就會有點問題,解決辦法:在join方法前再加一句執行interrupt方法,不過這個時候可能會有新的問題:執行interrupt方法后你的線程類會拋InterruptedException,上有政策下有對策,加一個開關變量做判斷就能完美解決,可參考下面的代碼:

Java代碼:

  1. /**    
  2.  * <p>Description: 創建線程的內部類</p>    
  3.  * @author cuishen    
  4.  * @version 1.1    
  5.  */    
  6. class NewThread implements Runnable {     
  7.     Thread t;     
  8.     NewThread() {     
  9.         t = new Thread(this, path);     
  10.         t.start();     
  11.     }     
  12.     public void run() {     
  13.         try {     
  14.             while(isThreadAlive) {     
  15.                 startMonitor();     
  16.                 Thread.sleep(Long.parseLong(controlList.get(controlList.size() - 1).toString()));     
  17.             }     
  18.         } catch (InterruptedException e) {     
  19.             if(!ifForceInterruptThread) {//開關變量     
  20.                 stopThread(logThread);     
  21.                 String error = "InterruptedException!!!" + path + ": Interrupted,線程異常終止!程序已試圖重啟該線程!!";     
  22.                 System.err.println(error);     
  23.                 LogService.writeLog(error);     
  24.                 createLogThread();     
  25.             }     
  26.         }     
  27.     }     
  28. }     
  29.     
  30. public void createLogThread() {     
  31.     ifForceInterruptThread = false;//開關變量     
  32.     logThread = new NewThread();     
  33. }     
  34.     
  35. private void stopThread(NewThread thread) {     
  36.     try {     
  37.         thread.t.join(100);     
  38.     } catch (InterruptedException ex) {     
  39.         System.out.println("線程終止異常!!!");     
  40.     } finally {     
  41.         thread = null;     
  42.     }     
  43. }     
  44.     
  45. /**    
  46.  * 關閉并徹底釋放該線程資源的方法    
  47.  */    
  48. public void stopThread() {     
  49.     try {     
  50.         ifForceInterruptThread = true;//開關變量     
  51.         isThreadAlive = false;     
  52.         logThread.t.interrupt();     
  53.         logThread.t.join(50);     
  54.     } catch (InterruptedException ex) {     
  55.         System.out.println("線程終止異常!!!");     
  56.     } finally {     
  57.         this.controlList = null;     
  58.         this.keyList = null;     
  59.         logThread = null;     
  60.     }     
  61. }   


對于繼承JFrame的窗體類,我們要注意在初始化方法中加入:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); ,并且注意和其關聯的事件監聽類一律寫成窗體類的內部類,這樣窗體dispose()的時候,這些內部類也一并銷毀,就不會再有什么莫名其妙的引用了。

 

【編輯推薦】

  1. 20個開發人員非常有用的Java功能代碼
  2. 走進Java 7中的模塊系統
  3. JavaFX 1.2 已經發布 主要新功能一覽
  4. 2009年十大Java技術解決方案
  5. 2008最值得學習的五種JAVA技術
責任編輯:仲衡 來源: Javaeye技術網站
相關推薦

2018-11-20 10:50:00

Java性能優化編程技巧

2010-11-15 16:20:33

Oracle系統優化

2009-01-08 19:11:39

服務器應用程序SQL Server

2013-12-17 17:05:20

iOS性能優化

2019-10-17 10:10:23

優化Web前端

2022-07-20 07:45:15

多線程程序性能

2025-05-08 09:11:41

2025-05-28 01:00:00

2016-12-28 11:23:59

優化iOS程序性

2024-12-05 15:33:50

Python列表元組

2010-08-10 13:58:00

Flex性能測試

2009-07-29 11:33:14

ASP.NET技巧ASP.NET應用程序

2009-01-08 19:06:13

服務器應用程序SQL Server

2009-01-08 19:14:37

服務器應用程序SQL Server

2010-02-04 09:41:03

Android應用程序

2020-08-10 17:49:25

JVM內存溢出

2022-05-05 11:21:00

程序優化

2023-04-13 07:33:31

Java 8編程工具

2023-03-27 07:39:07

內存溢出優化

2023-05-14 22:25:33

內存CPU
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧洲成人免费视频 | 亚洲免费在线观看av | 亚洲国产精品一区二区久久 | 亚洲午夜久久久 | 亚洲欧美日韩电影 | 天天舔天天 | 免费在线一区二区 | 成人在线精品视频 | 九九热国产精品视频 | 日韩在线不卡 | 羞羞视频免费观看入口 | se婷婷 | 亚洲精品久久久久久一区二区 | 亚洲二区在线 | 中文字幕在线观看一区二区 | 99日韩 | 人人干人人舔 | 自拍偷拍中文字幕 | 毛片网站免费观看 | 91精品国产高清久久久久久久久 | 免费观看一级特黄欧美大片 | 日韩一二区在线 | 欧美精品1区 | 日韩视频在线播放 | 日韩精品一区二区三区中文字幕 | 久综合| 国产成人精品999在线观看 | 免费性视频 | 亚洲一区中文字幕 | 国产乡下妇女做爰 | 在线伊人| 国产91亚洲精品 | 国产精品成人一区二区 | 国产精品美女www爽爽爽 | 日韩欧美手机在线 | 中文字幕在线一区 | 国产高清精品一区二区三区 | 欧美阿v| 凹凸日日摸日日碰夜夜 | 日韩一区和二区 | 国产精品高潮呻吟久久aⅴ码 |