ALL is Well!

          敏捷是一條很長的路,摸索著前進著

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            30 隨筆 :: 23 文章 :: 71 評論 :: 0 Trackbacks

          cglib版本為cglib-nodep-2.2.jar.
          本次只為演示在使用中出現的Java內存泄漏的問題,以及如何解決這樣的問題。
          cglib的應用是非常多的,但是當我們使用它的時候,如果一不小心,等出了問題再去查,就比較杯具了。所以最好的解決方案就是寫代碼時就注意這些細節。(當然了,不能指望在開發階段不引入Bug)
          近期項目在做壓力測試,暴露了內存泄漏的Bug,cglib的使用不當便是原因之一。
          下面來介紹代碼。
          清單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     * 創建一個類動態代理.
          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     * 實現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     * 實現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     * 測試代碼
          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); // 為了測試縮寫
          77            Thread.sleep(100);
          78            System.out.println(count++);
          79        }

          80    }

          81}

          用JProfiler來觀察內存對象情況。
          運行了一段時間(幾十秒鐘吧),內存對象的情況如圖所示:

          我們看到 MyFilter 的Instance count 已經達到了1266個,而且隨著程序的繼續運行,Instance count還在不斷飆升,此情此景讓人心寒。
          而且在JProfiler上點擊 Run GC 按鈕 這些對象并不會被回收。內存泄漏啦。

          原因就是cglib自身的內部代理類緩存,將MyFilter對象加入到了緩存中,以至于該對象很大、并發量很大時,會造成內存溢出的Bug。

          既然知道了原因,解決辦法就很明顯了。
          1.重寫MyFilter類的equals和hashCode方法,這樣,當MyFilter對象準備進入緩存時,cglib會判斷是否為不同的MyFilter對象,如果是才加入到緩存。
          我們重寫了equals和hashCode后,讓cglib認為這些MyFilter對象都是相同的。
          2.將MyFilter類設置為靜態類。原理都是相同的。

          我以第二種解決方案來修改代碼,請看。
          清單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;
                                                                 }

                                                             }
          ;
                                                             
          // 創建實例
              private static CallbackFilter    myFilter      = new MyFilter();

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


              
          /**
               * 實現CallbackFilter接口
               * 
               * 
          @author l
               
          */

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

              }


              
          /**
               * 創建一個類動態代理.
               * 
               * 
          @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();
              }


              
          /**
               * 測試代碼
               * 
               * 
          @param args
               * 
          @throws InterruptedException
               
          */

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

              }

          }


          運行后的結果應該很明顯了:


          MyFilter的Instance count 一直為1.
          問題解決了。

          因為我的MyFilter類中沒有成員變量,所以在多線程并發訪問時也不會出現問題。

          如果以方案1 來解決這個內存泄漏問題情況是怎樣的呢?
          清單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());
              }


              
          /**
               * 實現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// 沒什么原則,只為測試
                  }

              }


              
          /**
               * 創建一個類動態代理.
               * 
               * 
          @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();
              }


              
          /**
               * 測試代碼
               * 
               * 
          @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); // 為了測試縮寫
                      Thread.sleep(100);
                      System.out.println(count
          ++);
                  }

              }

          }


          運行一段時間后(幾十秒),JProfiler的觀測結果為:

          MyFilter的對象還是很多,這是不是就表明 內存泄漏的問題依然存在呢。
          當然不是,因為JVM垃圾回收策略的原因,我們new出來的MyFilter對象并不是 一旦成為垃圾就立即 被回收的。
          經觀察,當Instance count 為600左右時,GC會將這些“垃圾”回收。

          解決問題之際感慨一下:Java內存問題無處不在啊。

          本文為原創,歡迎轉載,轉載請注明出處BlogJava
          posted on 2010-09-09 17:00 李 明 閱讀(3674) 評論(1)  編輯  收藏

          評論

          # re: cglib使用不慎引發的Java內存泄漏 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++);
          }  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 宝丰县| 鄯善县| 如皋市| 沧州市| 小金县| 阿克| 姚安县| 叶城县| 外汇| 南川市| 周宁县| 阿尔山市| 宣化县| 民和| 大悟县| 安丘市| 敖汉旗| 耒阳市| 石林| 慈利县| 策勒县| 榆树市| 通许县| 澄江县| 疏勒县| 彭水| 铜山县| 泰州市| 宝丰县| 沂水县| 沐川县| 桂林市| 铜川市| 黑水县| 普安县| 屏南县| 赤峰市| 丹巴县| 丽水市| 右玉县| 花垣县|