ALL is Well!

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

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

          cglib版本為cglib-nodep-2.2.jar.
          本次只為演示在使用中出現(xiàn)的Java內(nèi)存泄漏的問(wèn)題,以及如何解決這樣的問(wèn)題。
          cglib的應(yīng)用是非常多的,但是當(dāng)我們使用它的時(shí)候,如果一不小心,等出了問(wèn)題再去查,就比較杯具了。所以最好的解決方案就是寫(xiě)代碼時(shí)就注意這些細(xì)節(jié)。(當(dāng)然了,不能指望在開(kāi)發(fā)階段不引入Bug)
          近期項(xiàng)目在做壓力測(cè)試,暴露了內(nèi)存泄漏的Bug,cglib的使用不當(dāng)便是原因之一。
          下面來(lái)介紹代碼。
          清單1:

           1package com.jn.proxy;
           2
           3import java.lang.reflect.Method;
           4
           5import net.sf.cglib.proxy.Callback;
           6import net.sf.cglib.proxy.CallbackFilter;
           7import net.sf.cglib.proxy.Enhancer;
           8import net.sf.cglib.proxy.MethodInterceptor;
           9import net.sf.cglib.proxy.MethodProxy;
          10import net.sf.cglib.proxy.NoOp;
          11
          12/**
          13 * 步驟方法攔截器.<br>
          14 * 
          15 */

          16public class CglibLeak1 {
          17
          18    public <T> T newProxyInstance(Class<T> clazz) {
          19        return newProxyInstance(clazz, new MyInterceptor(), new MyFilter());
          20    }

          21
          22    /**
          23     * 創(chuàng)建一個(gè)類(lèi)動(dòng)態(tài)代理.
          24     * 
          25     * @param <T>
          26     * @param superclass
          27     * @param methodCb
          28     * @param callbackFilter
          29     * @return
          30     */

          31    public static <T> T newProxyInstance(Class<T> superclass, Callback methodCb, CallbackFilter callbackFilter) {
          32        Enhancer enhancer = new Enhancer();
          33        enhancer.setSuperclass(superclass);
          34        enhancer.setCallbacks(new Callback[] { methodCb, NoOp.INSTANCE });
          35        enhancer.setCallbackFilter(callbackFilter);
          36
          37        return (T) enhancer.create();
          38    }

          39    
          40    /**
          41     * 實(shí)現(xiàn)MethodInterceptor接口
          42     * 
          43     * @author l
          44     *
          45     */

          46    class MyInterceptor implements MethodInterceptor {
          47        @Override
          48        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
          49            throws Throwable {
          50            return null;
          51        }

          52    }

          53    
          54    /**
          55     * 實(shí)現(xiàn)CallbackFilter接口
          56     * 
          57     * @author l
          58     */

          59    class MyFilter implements CallbackFilter {
          60        @Override
          61        public int accept(Method method) {
          62            // Do some thing
          63            return 1;
          64        }

          65    }

          66    
          67    /**
          68     * 測(cè)試代碼
          69     * @param args
          70     * @throws InterruptedException
          71     */

          72    public static void main(String args[]) throws InterruptedException {
          73        CglibLeak1 leak = new CglibLeak1();
          74        int count = 0;
          75        while(true{
          76            leak.newProxyInstance(Object.class); // 為了測(cè)試縮寫(xiě)
          77            Thread.sleep(100);
          78            System.out.println(count++);
          79        }

          80    }

          81}

          用JProfiler來(lái)觀(guān)察內(nèi)存對(duì)象情況。
          運(yùn)行了一段時(shí)間(幾十秒鐘吧),內(nèi)存對(duì)象的情況如圖所示:

          我們看到 MyFilter 的Instance count 已經(jīng)達(dá)到了1266個(gè),而且隨著程序的繼續(xù)運(yùn)行,Instance count還在不斷飆升,此情此景讓人心寒。
          而且在JProfiler上點(diǎn)擊 Run GC 按鈕 這些對(duì)象并不會(huì)被回收。內(nèi)存泄漏啦。

          原因就是cglib自身的內(nèi)部代理類(lèi)緩存,將MyFilter對(duì)象加入到了緩存中,以至于該對(duì)象很大、并發(fā)量很大時(shí),會(huì)造成內(nèi)存溢出的Bug。

          既然知道了原因,解決辦法就很明顯了。
          1.重寫(xiě)MyFilter類(lèi)的equals和hashCode方法,這樣,當(dāng)MyFilter對(duì)象準(zhǔn)備進(jìn)入緩存時(shí),cglib會(huì)判斷是否為不同的MyFilter對(duì)象,如果是才加入到緩存。
          我們重寫(xiě)了equals和hashCode后,讓cglib認(rèn)為這些MyFilter對(duì)象都是相同的。
          2.將MyFilter類(lèi)設(shè)置為靜態(tài)類(lèi)。原理都是相同的。

          我以第二種解決方案來(lái)修改代碼,請(qǐng)看。
          清單2:
          package com.jn.proxy;

          import java.lang.reflect.Method;

          import net.sf.cglib.proxy.Callback;
          import net.sf.cglib.proxy.CallbackFilter;
          import net.sf.cglib.proxy.Enhancer;
          import net.sf.cglib.proxy.MethodInterceptor;
          import net.sf.cglib.proxy.MethodProxy;
          import net.sf.cglib.proxy.NoOp;

          /**
           * 步驟方法攔截器.<br>
           
          */

          public class CglibLeak {

              
          private static MethodInterceptor myInterceptor = new MethodInterceptor() {
                                                                 @Override
                                                                 
          public Object intercept(Object obj,
                                                                     Method method, Object[] args,
                                                                     MethodProxy proxy) 
          throws Throwable {
                                                                     
          // do some things
                                                                     return null;
                                                                 }

                                                             }
          ;
                                                             
          // 創(chuàng)建實(shí)例
              private static CallbackFilter    myFilter      = new MyFilter();

              
          public static <T> T newProxyInstance(Class<T> clazz) {
                  
          return newProxyInstance(clazz, myInterceptor, myFilter);
              }


              
          /**
               * 實(shí)現(xiàn)CallbackFilter接口
               * 
               * 
          @author l
               
          */

              
          static class MyFilter implements CallbackFilter {
                  @Override
                  
          public int accept(Method method) {
                      
          // Do some thing
                      return 1;
                  }

              }


              
          /**
               * 創(chuàng)建一個(gè)類(lèi)動(dòng)態(tài)代理.
               * 
               * 
          @param <T>
               * 
          @param superclass
               * 
          @param methodCb
               * 
          @param callbackFilter
               * 
          @return
               
          */

              
          public static <T> T newProxyInstance(Class<T> superclass, Callback methodCb,
                  CallbackFilter callbackFilter) 
          {
                  Enhancer enhancer 
          = new Enhancer();
                  enhancer.setSuperclass(superclass);
                  enhancer.setCallbacks(
          new Callback[]{methodCb, NoOp.INSTANCE});
                  enhancer.setCallbackFilter(callbackFilter);

                  
          return (T)enhancer.create();
              }


              
          /**
               * 測(cè)試代碼
               * 
               * 
          @param args
               * 
          @throws InterruptedException
               
          */

              
          public static void main(String args[]) throws InterruptedException {
                  
          int count = 0;
                  
          while (true{
                      newProxyInstance(Object.
          class); // 為了測(cè)試縮寫(xiě)
                      Thread.sleep(100);
                      System.out.println(count
          ++);
                  }

              }

          }


          運(yùn)行后的結(jié)果應(yīng)該很明顯了:


          MyFilter的Instance count 一直為1.
          問(wèn)題解決了。

          因?yàn)槲业腗yFilter類(lèi)中沒(méi)有成員變量,所以在多線(xiàn)程并發(fā)訪(fǎng)問(wèn)時(shí)也不會(huì)出現(xiàn)問(wèn)題。

          如果以方案1 來(lái)解決這個(gè)內(nèi)存泄漏問(wèn)題情況是怎樣的呢?
          清單3:
          package com.jn.proxy;

          import java.lang.reflect.Method;

          import net.sf.cglib.proxy.Callback;
          import net.sf.cglib.proxy.CallbackFilter;
          import net.sf.cglib.proxy.Enhancer;
          import net.sf.cglib.proxy.MethodInterceptor;
          import net.sf.cglib.proxy.MethodProxy;
          import net.sf.cglib.proxy.NoOp;

          /**
           * 步驟方法攔截器.<br>
           
          */

          public class CglibLeak {

              
          private static MethodInterceptor myInterceptor = new MethodInterceptor() {
                                                                 @Override
                                                                 
          public Object intercept(Object obj,
                                                                     Method method, Object[] args,
                                                                     MethodProxy proxy) 
          throws Throwable {
                                                                     
          // do some things
                                                                     return null;
                                                                 }

                                                             }
          ;

              
          public <T> T newProxyInstance(Class<T> clazz) {
                  
          return newProxyInstance(clazz, myInterceptor, new MyFilter());
              }


              
          /**
               * 實(shí)現(xiàn)CallbackFilter接口
               * 
               * 
          @author l
               
          */

              
          class MyFilter implements CallbackFilter {
                  @Override
                  
          public int accept(Method method) {
                      
          // Do some thing
                      return 1;
                  }


                  @Override
                  
          public boolean equals(Object o) {
                      
          if (o instanceof MyFilter) {
                          
          return true;
                      }

                      
          return false;
                  }


                  @Override
                  
          public int hashCode() {
                      
          return 10011// 沒(méi)什么原則,只為測(cè)試
                  }

              }


              
          /**
               * 創(chuàng)建一個(gè)類(lèi)動(dòng)態(tài)代理.
               * 
               * 
          @param <T>
               * 
          @param superclass
               * 
          @param methodCb
               * 
          @param callbackFilter
               * 
          @return
               
          */

              
          public static <T> T newProxyInstance(Class<T> superclass, Callback methodCb,
                  CallbackFilter callbackFilter) 
          {
                  Enhancer enhancer 
          = new Enhancer();
                  enhancer.setSuperclass(superclass);
                  enhancer.setCallbacks(
          new Callback[]{methodCb, NoOp.INSTANCE});
                  enhancer.setCallbackFilter(callbackFilter);

                  
          return (T)enhancer.create();
              }


              
          /**
               * 測(cè)試代碼
               * 
               * 
          @param args
               * 
          @throws InterruptedException
               
          */

              
          public static void main(String args[]) throws InterruptedException {
                  CglibLeak l 
          = new CglibLeak();
                  
          int count = 0;
                  
          while (true{
                      l.newProxyInstance(Object.
          class); // 為了測(cè)試縮寫(xiě)
                      Thread.sleep(100);
                      System.out.println(count
          ++);
                  }

              }

          }


          運(yùn)行一段時(shí)間后(幾十秒),JProfiler的觀(guān)測(cè)結(jié)果為:

          MyFilter的對(duì)象還是很多,這是不是就表明 內(nèi)存泄漏的問(wèn)題依然存在呢。
          當(dāng)然不是,因?yàn)镴VM垃圾回收策略的原因,我們new出來(lái)的MyFilter對(duì)象并不是 一旦成為垃圾就立即 被回收的。
          經(jīng)觀(guān)察,當(dāng)Instance count 為600左右時(shí),GC會(huì)將這些“垃圾”回收。

          解決問(wèn)題之際感慨一下:Java內(nèi)存問(wèn)題無(wú)處不在啊。

          本文為原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處BlogJava
          posted on 2010-09-09 17:00 李 明 閱讀(3674) 評(píng)論(1)  編輯  收藏

          評(píng)論

          # re: cglib使用不慎引發(fā)的Java內(nèi)存泄漏 2010-09-09 20:16 pxb

          Enhancer enhancer = new Enhancer();
          enhancer.setSuperclass(Object.class);
          enhancer.setCallbacks(new Callback[]{methodCb, NoOp.INSTANCE});
          enhancer.setCallbackFilter(callbackFilter);

          while (true) {
          enhancer.create();
          Thread.sleep(100);
          System.out.println(count++);
          }  回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 诸城市| 青田县| 广东省| 岳阳市| 景谷| 随州市| 潮安县| 都兰县| 赞皇县| 铁岭市| 绍兴市| 阿尔山市| 将乐县| 手机| 西林县| 左云县| 蓬安县| 呼玛县| 疏勒县| 罗江县| 大名县| 河源市| 洛川县| 宁波市| 班戈县| 麦盖提县| 莱州市| 湟源县| 利津县| 高州市| 绵阳市| 工布江达县| 昌宁县| 美姑县| 南澳县| 广德县| 铜陵市| 界首市| 桐乡市| 饶阳县| 游戏|