<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I want to fly higher
    programming Explorer
    posts - 114,comments - 263,trackbacks - 0

    Reloading Java Classes 201: How do ClassLoader leaks happen?

    If you have programmed in Java for some time you know that memory leaks do happen. Usually it’s the case of a collection somewhere with references to objects (e.g. listeners) that should have been cleared, but never were. Classloaders are a very special case of this, and unfortunately, with the current state of the Java platform, these leaks are both inevitable and costly: routinely causing OutOfMemoryError’s in production applications after just a few redeploys.

    Let’s get started. Recalling RJC101: to reload a class we threw away the old classloader and created a new one, copying the object graph as best we could:



    Every object had a reference to its class, which in turn had a reference to its classloader. However we didn’t mention that every classloader in turn has a reference to each of the classes it has loaded, each of which holds static fields defined in the class:


    This means that

    1. If a classloader is leaked it will hold on to all its classes and all their static fields. Static fields commonly hold caches, singleton objects, and various configuration and application states. Even if your application

      doesn’t have any large static caches, it doesn’t mean that the framework you use doesn’t hold them for you (e.g. Log4J is a common culprit as it’s often put in the server classpath). This explains why leaking a

      classloader can be so expensive.

    2. To leak a classloader it’s enough to leave a reference to any object, created from a class, loaded by that classloader. Even if that object seems completely harmless (e.g. doesn’t have a single field), it will still hold on

      to its classloader and all the application state. A single place in the application that survives the redeploy and doesn’t do a proper cleanup is enough to sprout the leak. In a typical application there will be several such

      places, some of them almost impossible to fix due to the way third-party libraries are built. Leaking a classloader is therefore, quite common.

    To examine this from a different perspective let’s return to the code example from our previous article. Breeze through it to quickly catch up.

    Introducing the Leak

    We will use the exact same Main class as before to show what a simple leak could look like:

    public class Main {

      
    private static IExample example1;

      
    private static IExample example2;

      
    public static void main(String[] args)  {

        example1 = ExampleFactory.newInstance().copy();

        
    while (true) {

          example2 = ExampleFactory.newInstance().copy();

          System.out.println("1) " +

            example1.message() + " = " + example1.plusPlus());

          System.out.println("2) " +

            example2.message() + " = " + example2.plusPlus());

          System.out.println();

          Thread.currentThread().sleep(3000);
        }
      }
    }

    The ExampleFactory class is also exactly the same, but here’s where things get leaky. Let’s introduce a new class called Leak and a corresponding interface ILeak:

    interface ILeak {
    }

    public class Leak implements ILeak {

      
    private ILeak leak;

      
    public Leak(ILeak leak) {

        
    this.leak = leak;
      }
    }

    As you can see it’s not a terribly complicated class: it just forms a chain of objects, with each doing nothing more than holding a reference to the previous one. We will modify the Exampleclass to include a reference to the Leak object and throw in a large array to take up memory (it represents a large cache). Let’s omit some methods shown in the previous article for brevity:

    public class Example implements IExample {

      
    private int counter;

      
    private ILeak leak;

      
    private static final long[] cache = new long[1000000];

      
    /* message(), counter(), plusPlus() impls */

      
    public ILeak leak() {

        
    return new Leak(leak);
      }

      
    public IExample copy(IExample example) {

        
    if (example != null) {

          counter = example.counter();

          leak = example.leak();
        }
        
    return this;
      }
    }

    The important things to note about Example class are:

    1. Example holds a reference to Leak, but Leak has no references to Example.

    2. When Example is copied (method copy() is called) a new Leak object is created holding a reference to the previous one.

    If you try to run this code an OutOfMemoryError will be thrown after just a few iterations:

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 	at example.Example.<clinit>(Example.java:8)

    With the right tools, we can look deeper and see how this happens.

    Post Mortem

    Since Java 5.0, we’ve been able to use the jmap command line tool included in the JDK distribution to dump the heap of a running application (or for that matter even extract the Java heap from a core dump). However, since our application is crashing we will need a feature that was introduced in Java 6.0: dumping the heap on OutOfMemoryError. To do that we only need to add -XX:+HeapDumpOnOutOfMemoryError to the JVM command line:

    java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid37266.hprof ... Heap dump file created [57715044 bytes in 1.707 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 	at example.Example.<clinit>(Example.java:8)

    After we have the heap dump we can analyze it. There are a number of tools (including jhat, a small web-based analyzer included with the JDK), but here we will use the more sophisticatedEclipse Memory Analyzer (EMA).

    After loading the heap dump into the EMA we can look at the Dominator Tree analysis. It is a very useful analysis that will usually reliably identify the biggest memory consumers in the heap and what objects hold a reference to them. In our case it seems quite obvious that the Leakclass is the one that consumes most of the heap:


    Now let’s run a search for all of the Leak objects and see what are they holding to. To do that we run a search List objects -> with outgoing references for “example.Leak”:


    The results include several Leak objects. Expanding the outgoing references we can see that each of them holds on to a separate instance of Example through a bunch of intermediate objects:


    You may notice that one of the intermediate objects is ExampleFactory$1, which refers to the anonymous subclass of URLClassLoader we created in the ExampleFactory class. In fact what is happening is exactly the situation we described in the beginning of the article:

    • Each Leak object is leaking. They are holding on to their classloaders

    • The classloaders are holding onto the Example class they have loaded:


    Conclusions

    Though this example is slightly contrived, the main idea to take away is that it’s easy to leak a single object in Java. Each leak has the potential to leak the whole classloader if the application is redeployed or otherwise a new classloader is created. Since preventing such leaks is very challenging, it’s a better idea to use Eclipse Memory Analyzer and your understanding of classloaders to hunt them down after you get an OutOfMemoryError on redeploy.

    This article addressed the following questions:

    • How does reloading a class cause the classloader to leak?

    • What are some consequences of leaking classloaders?

    • What tools can be used to troubleshoot these memory leaks



      ___________________________________________________________________________________________________________________

      筆記:
          1.每一個對象都持有一個指向自己class的引用---指向加載自己的classloader

          2.每一個classloader持有所有加載的class中的靜態(tài)字段

      _______________________________________________________________________________________________________________________

      本地代碼測試:

      package com.mavsplus.example.java.rjc;

      import java.util.concurrent.TimeUnit;

      /**
       * <a href= "http://www.zeroturnaround.com/blog/rjc201/" >rjc series 2</>
       * 
       * 測試因為reload引起的"內(nèi)存泄露"問題 --->注:LeakExample.class和Leak.class均是由urlclassloader加載
       * 
       * <pre>
       *     泄露原因分析:

       *     1.主循環(huán)中每次reload example2的時候,新實例的leak引用都會指向舊的實例leak引用,從而形成一個"鏈"

       *     2.每一個leak實例都會有一個指向自己class的引用,即Leak-->Leak.class

       *     3.因為Leak.class是由匿名的urlclassloader加載->即ExampleFactory$3->class是有一個指向加載自己的classloader的引用的->

       *         -->即Leak.class->ExampleFactory$3(匿名urlclassloader)->

       *     4.而ExampleFactory$3(匿名urlclassloader)則持有其加載的所有class,而class則包括所有的靜態(tài)字段則本例中則是指LeakExample中的cache

       *         即ExampleFactory$3->LeakExample.class

       *     5.即文檔中的描述    

       *  LeakExample.class            LeakExample.class            LeakExample.class    
       *           /|\                            /|\                            /|\
       *     ExampleFactory$3            ExampleFactory$3            ExampleFactory$3
       *           /|\                            /|\                            /|\
       *         Leak.class                    Leak.class                    Leak.class
       *           /|\                           /|\                         /|\
       *         Leak------------------------->Leak------------------------->Leak

       *     6.如何避免reload引起的泄露問題

       *         1.狀態(tài)復(fù)制時,不要引起類似本例的"鏈?zhǔn)揭?問題

       *         2.狀態(tài)復(fù)制時要用深度克隆,則不會引起問題.

               {@link RJCAvoidLeakTest}

       * </pre>
       * 
       * <pre>
       *     vm-arguments:

       *         -Xms32m -Xmx32m -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=C:\Users\pc
       * </pre>
       * 
       * <pre>

       *     輸出:

       *     1)Version 1 = 0

       *     2)Version 1 = 0
       * 
       *     1)Version 1 = 1

       *     2)Version 1 = 1
       * 
       *     java.lang.OutOfMemoryError: Java heap space
       *     Dumping heap to C:\Users\pc\java_pid7564.hprof 

       *     Heap dump file created [25453917 bytes in 0.033 secs]

       *     Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

       *         at com.mavsplus.example.java.rjc.LeakExample.<clinit>(LeakExample.java:13)

       *         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

       *         at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

       *         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)

       *         at java.lang.reflect.Constructor.newInstance(Unknown Source)

       *         at java.lang.Class.newInstance(Unknown Source)

       *         at com.mavsplus.example.java.rjc.ExampleFactory.newInstance3(ExampleFactory.java:69)

       *         at com.mavsplus.example.java.rjc.RJCLeakTest.main(RJCLeakTest.java:37)
       * </pre>
       * 
       * @author landon
       * @since 1.8.0_25
       
      */
      public class RJCLeakTest {

          
      private static ILeakExample example1;

          
      private static ILeakExample example2;

          
      public static void main(String[] args) throws Exception {

              example1 = ExampleFactory.newInstance3();

              
      while (true) {
                  example2 = ExampleFactory.newInstance3().copy(example2);

                  System.out.println("1)" + example1.message() + " = " + example1.plusPlus());

                  System.out.println("2)" + example2.message() + " = " + example2.plusPlus());

                  System.out.println();


                  
      // 加速"泄露",每5ms執(zhí)行一次

                  TimeUnit.MILLISECONDS.sleep(5);
              }
          }
      }




      package com.mavsplus.example.java.rjc;

      import java.io.Serializable;

      /**
       * "泄露對象"接口,用來測試reload引起的"內(nèi)存泄露"
       * 
       * @author landon
       * @since 1.8.0_25
       
      */
      public interface ILeak extends Serializable{
          
          
      public Object deepCopy();
      }




      package com.mavsplus.example.java.rjc;

      import java.io.ByteArrayInputStream;

      import java.io.ByteArrayOutputStream;

      import java.io.IOException;

      import java.io.ObjectInputStream;

      import java.io.ObjectOutputStream;

      /**
       * "泄露"對象
       * 
       * @author landon
       
      */
      public class Leak implements ILeak {

          
      private ILeak leak;

          
      public Leak(ILeak leak) {

              
      this.leak = leak;

          }


          
      // 用序列化方式實現(xiàn)了深度克隆

          
      // 因為本例的特殊性,Leak中套了一個leak-->所以用clone方法還是會有"鏈?zhǔn)揭?的問題,所以采用此種方式

          
      // --->但是依然會報錯
          @Override

          
      public Object deepCopy() {
              
      try {
                  ByteArrayOutputStream bo = new ByteArrayOutputStream();

                  ObjectOutputStream oo = new ObjectOutputStream(bo);

                  oo.writeObject(this);

                  ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());

                  ObjectInputStream oi = new ObjectInputStream(bi);

                  
      return (oi.readObject());

              } catch (IOException | ClassNotFoundException e) {

                  e.printStackTrace();
              }

              
      return null;
          }
      }



      package com.mavsplus.example.java.rjc;

      /**
       * 可"泄露"接口
       * 
       * 
      @author landon
       * 
      @since 1.8.0_25
       
      */
      public interface ILeakExample {

          
      public String message();

          
      public int plusPlus();

          
      public int counter();

          
      public ILeakExample copy(ILeakExample example);

          
      public ILeak leak();
          
          
      public ILeakExample deepCopy(ILeakExample example);
      }




      package com.mavsplus.example.java.rjc;

      /**
       * "泄露的"Example對象
       * 
       * @author landon
       
      */
      public class LeakExample implements ILeakExample {

          
      private int counter;

          
      // 表示一個大的cache-用來"加快內(nèi)存泄露"--->注意是static字段,表示屬于"類"的
          private static final long[] cache = new long[1000000];

          
      // 增加一個"泄露"對象
          private ILeak leak;

          @Override

          
      public ILeak leak() {

              
      return new Leak(leak);

          }

          @Override

          
      public String message() {

              
      return "Version 1";

              
      // return "Version 2";
          }

          @Override

          
      public int plusPlus() {

              
      return counter++;
          }

          @Override

          
      public int counter() {

              
      return counter;
          }

          @Override

          
      public ILeakExample copy(ILeakExample example) {

              
      if (example != null) {

                  counter = example.counter();

                  
      // 注意這塊的代碼很關(guān)鍵.每次調(diào)用copy的時候,調(diào)用leak方法->而leak方法會傳入之前實例對象的leak引用.

                  
      // 也就是說形成一個"鏈?zhǔn)?,所有LeakExample中的leak引用都指向之前構(gòu)造的實例中的leak引用.
                  leak = example.leak();
              }

              
      return this;
          }

          
          @Override
          
      public ILeakExample deepCopy(ILeakExample example) {

              
      if (example != null) {

                  counter = example.counter();

                  leak = (ILeak)example.leak().deepCopy();
              }

              
      return this;
          }
      }




      package com.mavsplus.example.java.rjc;

      import java.util.concurrent.TimeUnit;

      /**
       * <a href= "http://www.zeroturnaround.com/blog/rjc201/" >rjc series 2</>
       * 
       * 測試因為reload引起的"內(nèi)存泄露"問題-->注,這個test中的urlclassloader只加載LeakExample.class而不加載
       * 
       * <pre>
       *     泄露原因分析:

       *     1.首先說明該示例代碼和RJCLeakTest有一點不同,上一個例子urlclassloader不進(jìn)加載LeakExample,還加載了Leak.class,泄露原因已分析.但是本示例的urlclassloader并未

       *       加載Leak.class,但是從堆棧看到,還是發(fā)生了內(nèi)存溢出,why

       *  2.主要原因還是Leak對象的鏈?zhǔn)揭脝栴},可以看到鏈?zhǔn)揭玫臄?shù)目已經(jīng)到了5373個-->

       *      -->從EMA分析工具的dominator_tree中可以看到,每一個Leak對象的Shallow Heap為16,最后加載的LeakExample持有的Leak引用的Retained Heap(所有的鏈?zhǔn)揭?為

       *          --->16 * 5373 = 85968
       *          --->而測試?yán)又凶畲蠖咽怯邢薜?->于是便內(nèi)存溢出了
       * </pre>
       * 
       * <pre>
       *     vm-arguments:

       *         -Xms32m -Xmx32m -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=C:\Users\pc
       * </pre>
       * 
       * <pre>
       *     輸出:
       *     
       *     1)Version 1 = 5370
       *     2)Version 1 = 5370
       * 
       *     1)Version 1 = 5371
       *     2)Version 1 = 5371
       * 
       *     1)Version 1 = 5372
       *     2)Version 1 = 5372
       * 
       *     1)Version 1 = 5373
       *     2)Version 1 = 5373
       * 
       *     java.lang.OutOfMemoryError: Java heap space
       *     Dumping heap to C:\Users\pc\java_pid7652.hprof 

       *     Heap dump file created [25576205 bytes in 0.033 secs]

       *     Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

       *         at com.mavsplus.example.java.rjc.LeakExample.<clinit>(LeakExample.java:13)

       *         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

       *         at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

       *         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)

       *         at java.lang.reflect.Constructor.newInstance(Unknown Source)

       *         at java.lang.Class.newInstance(Unknown Source)

       *         at com.mavsplus.example.java.rjc.ExampleFactory.newInstance2(ExampleFactory.java:49)

       *         at com.mavsplus.example.java.rjc.RJCLeakTest2.main(RJCLeakTest2.java:34)
       * </pre>
       * 
       * @author landon
       * @since 1.8.0_25
       
      */
      public class RJCLeakTest2 {

          
      private static ILeakExample example1;

          
      private static ILeakExample example2;

          
      public static void main(String[] args) throws Exception {

              example1 = ExampleFactory.newInstance2();

              
      while (true) {

                  example2 = ExampleFactory.newInstance2().copy(example2);

                  System.out.println("1)" + example1.message() + " = " + example1.plusPlus());

                  System.out.println("2)" + example2.message() + " = " + example2.plusPlus());

                  System.out.println();

                  
      // 加速"泄露",每5ms執(zhí)行一次

                  TimeUnit.MILLISECONDS.sleep(5);
              }
          }
      }





      package com.mavsplus.example.java.rjc;

      import java.util.concurrent.TimeUnit;

      /**
       * <a href= "http://www.zeroturnaround.com/blog/rjc201/" >rjc series 2</>
       * 
       * 測試如何【避免】因為reload引起的"內(nèi)存泄露"問題 --->注:LeakExample.class和Leak.class均是由urlclassloader加載
       * 
       * <pre>
       *     1.本例復(fù)制狀態(tài)時采取了深拷貝的方式,避免了因"鏈?zhǔn)揭?引起的持有classloader無法gc的問題

       *     2.但是因為本例的特殊性,這個"鏈?zhǔn)?引用是無法解決的->所以造成序列化的時候會出現(xiàn)StackOverflowError
       * </pre>
       * 
       * <pre>
       *     vm-arguments:
       *         -Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=C:\Users\pc
       * </pre>
       * 
       * <pre>
       *     輸出:
       *     1)Version 1 = 3300
          2)Version 1 = 3300

          1)Version 1 = 3301
          2)Version 1 = 3301
          
          1)Version 1 = 3302
          2)Version 1 = 3302
          
          1)Version 1 = 3303
          2)Version 1 = 3303
          
          1)Version 1 = 3304
          2)Version 1 = 3304
          
          Exception in thread "main" java.lang.StackOverflowError

              at java.io.ObjectInputStream.readObject0(Unknown Source)

              at java.io.ObjectInputStream.defaultReadFields(Unknown Source)

              at java.io.ObjectInputStream.readSerialData(Unknown Source)

              at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)

              at java.io.ObjectInputStream.readObject0(Unknown Source)

              at java.io.ObjectInputStream.defaultReadFields(Unknown Source)

              at java.io.ObjectInputStream.readSerialData(Unknown Source)
       * </pre>
       * 
       * @author landon
       * @since 1.8.0_25
       
      */
      public class RJCAvoidLeakTest {

          
      private static ILeakExample example1;

          
      private static ILeakExample example2;

          
      public static void main(String[] args) throws Exception {

              example1 = ExampleFactory.newInstance3();

              
      while (true) {

                  example2 = ExampleFactory.newInstance3().deepCopy(example2);

                  System.out.println("1)" + example1.message() + " = " + example1.plusPlus());

                  System.out.println("2)" + example2.message() + " = " + example2.plusPlus());

                  System.out.println();

                  
      // 加速"泄露",每5ms執(zhí)行一次

                  TimeUnit.MILLISECONDS.sleep(5);
              }
          }
      }














      _____________________________________________________________________________________________

      關(guān)于EMA中shallow heap、retained heap,gc root的知識,請參考:

          
      1. https://www.yourkit.com/docs/java/help/sizes.jsp


      2. http://www.yourkit.com/docs/80/help/gc_roots.jsp











    posted on 2015-12-24 16:20 landon 閱讀(2827) 評論(0)  編輯  收藏 所屬分類: HotSwapClassLoader
    主站蜘蛛池模板: 免费一级做a爰片性色毛片| 国产一精品一AV一免费孕妇 | 国产精品亚洲专区一区| 久久精品国产亚洲AV未满十八| 国产AV日韩A∨亚洲AV电影 | 日韩成人免费视频| 四虎最新永久免费视频| 精品少妇人妻AV免费久久洗澡 | 亚洲精品蜜桃久久久久久| 久久丫精品国产亚洲av| 亚洲愉拍一区二区三区| 三年片在线观看免费观看大全中国| 国产在线精品一区免费香蕉| 亚洲视频免费播放| 国产一卡二卡≡卡四卡免费乱码| 亚洲国产精品一区二区第一页 | 亚洲精品一卡2卡3卡三卡四卡| 亚洲小说图区综合在线| 精品一区二区三区免费视频| 91青青国产在线观看免费| 日本高清免费aaaaa大片视频| 中文字幕亚洲一区| 亚洲人成7777影视在线观看| 污污污视频在线免费观看| 久久国产精品2020免费m3u8| 免费看又爽又黄禁片视频1000| 亚洲精品午夜国产VA久久成人| 中文文字幕文字幕亚洲色| 国产免费区在线观看十分钟 | 亚洲网站免费观看| 亚洲精品无码久久毛片| 亚洲狠狠ady亚洲精品大秀| 日韩精品免费一线在线观看 | 啦啦啦中文在线观看电视剧免费版| 亚洲日本韩国在线| 亚洲人xxx日本人18| a级毛片毛片免费观看永久| 在线中文高清资源免费观看| 久久亚洲高清观看| 美女黄色免费网站| 67194成是人免费无码|