【问题标题】:How classloader leak is occured类加载器泄漏是如何发生的
【发布时间】:2021-06-06 17:36:52
【问题描述】:

很抱歉向您提出这个基本问题,但我无法理解这个概念。 我读了很多 SO 帖子,但我无法理解。能否请您给我代码示例以了解。

  1. 正如post 中所说

加载类时无法选择静态变量进行垃圾回收。当相应的类加载器(负责加载此类)本身被收集为垃圾时,它们可以被收集。

我理解根据理论,如果 Classloader 有参考,则无法收集它,但我不明白它实际上是如何实现的。 能否请您用代码示例解释一下?

非常感谢您的帮助!

【问题讨论】:

    标签: java memory-management garbage-collection


    【解决方案1】:

    我正在发布我的理解,希望对您有所帮助, 背景理解: 理解这一点的简单方法是以 Tomcat 或任何此类应用程序为例。这是基于java的。 Tomcat 可以运行多个 webapp。即使您以不同的名称部署相同的应用程序,它们也会受到不同的对待。在这里,这两个应用程序将具有相同的类,但它们的处理方式仍然不同。所以这里是类加载器。 所以你可以这样想,就像 Tomcat 为每个应用程序创建一个类加载器并在其下加载它们。

    加载器的回收:如果Tomcat持有对加载器对象的引用,那么加载器对象将不会被回收。除非加载器得到垃圾收集,否则它加载的类会保留下来。 因此,如果您关闭一个应用程序,Tomcat 最终会引用它的相应加载器,以便 gc 可以将其回收为一个干净的它,包括由它加载的类。

    可能有帮助的快速链接: https://stackoverflow.com/questions/2433261/when-and-how-are-classes-garbage-collected-in-java#:~:text=A%20class%20in%20Java%20can,that%20class%20are%20still%20reachable.

    https://www.dynatrace.com/resources/ebooks/javabook/class-loader-issues/#:~:text=Classloader%20Cannot%20Be%20Garbage-Collected,hold%20references%20to%20their%20classes.

    【讨论】:

      【解决方案2】:

      让我们看看这段代码来了解类加载器是如何泄漏的

      Main.java

          public class Main {
          public static void main(String...args) throws Exception {
           List<Object> list = new ArrayList<>();
          loadClass(list);
          while (true) {
              System.gc();
              Thread.sleep(1000);
            }
         }
      
         private static void loadClass(List list) throws Exception {
          URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
          MyCustomClassLoader cl = new MyCustomClassLoader(url);
          Class<?> clazz = cl.loadClass("com.test.Foo");
          list.add(clazz.newInstance());
          cl = null;
            }
          }
      
         class MyCustomClassLoader extends URLClassLoader {
           public MyCustomClassLoader(URL... urls) {
           super(urls, null);
           }
          @Override
           protected void finalize() {
          System.out.println("*** CustomClassLoader finalized!");
           }
          }
      

      Foo.java

          public class Foo {
            public Foo() {
            System.out.println("Test ClassLoader: " + this.getClass().getClassLoader());
             }
           @Override
           protected void finalize() {
             System.out.println( this + " finalized!");
              }
          }
      

      这个的输出如下:

      测试类加载器:com.test.MyCustomClassLoader@71dac704

      所以,在这里我们可以看到“*** CustomClassLoader finalized!”没有被调用,这是因为 MyCustomClassLoader 持有对象列表的引用,因为类加载器加载的实例保存在其中。

      现在,让我们稍微修改一下代码,所以这里我们将 list 设置为 null

       public static void main(String...args) throws Exception {
           List<Object> list = new ArrayList<>();
          loadClass(list);
          while (true) {
              System.gc();
              Thread.sleep(1000);
              list = null;
          }
        }
      

      现在看看输出

      测试类加载器:com.test.MyCustomClassLoader@71dac704 com.test.Foo@650de12 敲定! *** CustomClassLoader 完成!

      【讨论】:

        猜你喜欢
        • 2017-02-26
        • 1970-01-01
        • 2014-05-14
        • 2012-03-08
        • 2011-03-16
        • 1970-01-01
        • 2022-11-28
        • 1970-01-01
        • 2018-11-24
        相关资源
        最近更新 更多