ALL is Well!

          敏捷是一條很長(zhǎng)的路,摸索著前進(jìn)著

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            30 隨筆 :: 23 文章 :: 71 評(píng)論 :: 0 Trackbacks

          最近一段時(shí)間,在公司里對(duì)java內(nèi)存泄露的問(wèn)題進(jìn)行了調(diào)查。

          問(wèn)題的發(fā)現(xiàn):

          系統(tǒng)中在連續(xù)不停地、反復(fù)進(jìn)行一個(gè)操作(先打開(kāi)A,然后切替到畫(huà)面B,點(diǎn)擊畫(huà)面履歷再回到A,如此反復(fù))。經(jīng)過(guò)長(zhǎng)時(shí)間的測(cè)試,經(jīng)常會(huì)20小時(shí),JVM的內(nèi)存使用量增長(zhǎng)30M以上。

           

          問(wèn)題的分析:

          首先根據(jù)操作,找到會(huì)執(zhí)行的代碼,對(duì)代碼進(jìn)行分析。

          Java會(huì)產(chǎn)生內(nèi)存泄露的原因,經(jīng)過(guò)本次調(diào)查,

          1.對(duì)于打開(kāi)的socket等資源,沒(méi)有做及時(shí)的回收處理。
          2.生存周期較長(zhǎng)的對(duì)象,持有了生存周期較短的對(duì)象的引用,以至于那些生存周期短的對(duì)象,在無(wú)用的情況下,沒(méi)有得到回收。
          3.對(duì)于類(lèi)的成員變量為集合的情況,對(duì)集合的使用應(yīng)該謹(jǐn)慎。比如,一個(gè)專(zhuān)門(mén)保存用戶操作履歷的對(duì)象,有全局變量List來(lái)保存用戶所有點(diǎn)擊過(guò)的鏈接。但實(shí)際項(xiàng)目中,不可能保存住用戶的每一次鏈接操作,然后顯示給用戶,有時(shí)候可能只是顯示最新的20條。所以這時(shí)候就要對(duì)這個(gè)全局變量進(jìn)行處理,不能讓它無(wú)限的膨脹下去。
          4.在類(lèi)的成員變量為集合的情況,集合中的元素又是比較復(fù)雜的對(duì)象,(這個(gè)對(duì)象中可能還包含著是集合的成員變量)在不需要此類(lèi)的對(duì)象的時(shí)候,應(yīng)該自己來(lái)實(shí)現(xiàn)對(duì)類(lèi)的成員的銷(xiāo)毀。如:
          1Iterator itor = myMap.keySet().iterator();   
          2while (itor.hasNext()) {   
          3    MyObject selectedInfo = (MyObject) itor.next();   
          4    selectedInfo.destroy();// 假設(shè)MyObject里有destroy方法,對(duì)MyObjec中的成員進(jìn)行銷(xiāo)毀   
          5    selectedInfo = null;   
          6}
             
          7myMap.clear();  

          5.對(duì)單態(tài)模式應(yīng)該慎用,對(duì)象在被初始化后將在JVM的整個(gè)生命周期中存在如果單態(tài)對(duì)象持有外部對(duì)象的引用,那么這個(gè)外部對(duì)象將不能被回收,如故這個(gè)外部對(duì)象很龐大,那么對(duì)內(nèi)存的消耗是很大的。
          6.雖然寫(xiě)java程序,有GC幫助我們管理內(nèi)存,但好的編程習(xí)慣還是需要的,可以避免不必要的麻煩。

          雖然寫(xiě)java程序,有GC幫助我們管理內(nèi)存,但好的編程習(xí)慣還是需要的,可以避免不必要的麻煩。

          1.復(fù)雜的對(duì)象,在不需要的情況下,最好能實(shí)現(xiàn)對(duì)它的成員的銷(xiāo)毀,然后再將其賦為null
          2.對(duì)于打開(kāi)的流,一定要做及時(shí)的處理。另外對(duì)于HttpURLConnection對(duì)象,連接后,要調(diào)用它的disconnect(),不要對(duì)資源進(jìn)行不必要的浪費(fèi)。
          3.盡量少用全局變量。
          4.在哪里生成對(duì)象,就在哪里銷(xiāo)毀它。
          5.盡量避免對(duì)象之間的相互引用。

          最后,記述一下我記錄內(nèi)存的方法。

          由于對(duì)代碼做好修改之后,要確認(rèn)一下內(nèi)存是否有明顯增長(zhǎng)。

          于是寫(xiě)一段代碼,每個(gè)5分鐘對(duì)對(duì)內(nèi)存進(jìn)行一次記錄,在連續(xù)運(yùn)行20小時(shí)候,做成曲線圖,以便分析。

          (以下是為了方便,重新寫(xiě)的,原來(lái)項(xiàng)目中用到的,有一整套完備的定時(shí)器生成和起動(dòng)的管理類(lèi),這里沒(méi)有寫(xiě)出來(lái)。)

           1import java.io.DataOutputStream;   
           2import java.io.FileOutputStream;   
           3import java.io.IOException;   
           4public class MemoryCollect {   
           5    public static void main(String args[]) {   
           6        MemoryCollector mc = new MemoryCollector(300000);   
           7        mc.start();   
           8    }
             
           9}
             
          10class MemoryCollector extends java.util.TimerTask {   
          11    private static final java.text.NumberFormat nf = java.text.NumberFormat   
          12            .getPercentInstance();   
          13    private static final java.text.DateFormat df = new java.text.SimpleDateFormat(   
          14            "yyyy-MM-dd HH:mm:ss");   
          15    private DataOutputStream dos = null;   
          16    private long period = 0;   
          17    private java.util.Timer timer = null;   
          18    MemoryCollector(long p) {   
          19        period = p;   
          20        timer = new java.util.Timer();   
          21    }
             
          22    public void start() {   
          23        timer.schedule(this0, period);   
          24    }
             
          25    public void run() {   
          26        System.gc();   
          27        Runtime imp = Runtime.getRuntime();   
          28        imp.totalMemory();   
          29        long totol = imp.totalMemory() / 1024;   
          30        long free = imp.freeMemory() / 1024;   
          31        try {   
          32            dos = new DataOutputStream(new FileOutputStream("D:\\memory.txt",   
          33                    true));   
          34            String date = df.format(new java.util.Date());   
          35            String info = date + "\t" + totol + "\t" + free + "\t"  
          36                    + nf.format((double) free / (double) totol);   
          37            System.out.println(info);   
          38            dos.writeUTF(info + "\r");   
          39            dos.flush();   
          40            dos.close();   
          41            dos = null;   
          42        }
           catch (Exception e) {   
          43            System.out.println(e);   
          44        }
           finally {   
          45            try {   
          46                if (dos != null{   
          47                    dos.flush();   
          48                    dos.close();   
          49                    dos = null;   
          50                }
             
          51            }
           catch (IOException e) {   
          52                e.printStackTrace();   
          53            }
             
          54        }
             
          55    }
             
          56    public void stop() {   
          57        super.cancel();   
          58        if (timer != null{   
          59            timer.cancel();   
          60            timer = null;   
          61        }
             
          62    }
             
          63}
           


          ----2009年02月02日
          posted on 2010-09-01 11:31 李 明 閱讀(1063) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 技術(shù)知識(shí)
          主站蜘蛛池模板: 阜新市| 天台县| 延寿县| 尼木县| 志丹县| 无为县| 清镇市| 尉犁县| 邮箱| 哈巴河县| 犍为县| 安龙县| 临沭县| 赣榆县| 丹阳市| 和平区| 临漳县| 平罗县| 东安县| 馆陶县| 海南省| 突泉县| 富平县| 庐江县| 罗平县| 神农架林区| 万安县| 思茅市| 长兴县| 扶绥县| 海原县| 枣强县| 宁都县| 常宁市| 太白县| 元氏县| 西乡县| 砚山县| 肇庆市| 阿坝县| 鄢陵县|