类加载器泄漏是如何发生的
How classloader leak is occured
很抱歉问你这个基本问题,但我无法理解这个概念。
我读了很多 SO post 但我无法理解。能否请你给我代码示例来理解。
- 如本文所述post
Static variables cannot be elected for garbage collection while the class is loaded. They can be collected when the respective class loader (that was responsible for loading this class) is itself collected for garbage.
理论上我理解如果有引用就不能收集Classloader,但我不明白这在实践中是如何实现的。
能否请您用代码示例进行解释?
非常感谢您的帮助!
发表我的理解希望对你有帮助,
背景理解:
理解这一点的简单方法是以 Tomcat 或任何此类应用程序为例。这是基于 java 的。
Tomcat 可以 运行 多个网络应用程序。即使您使用不同的名称部署相同的应用程序,它们也会受到不同的对待。在这里,这两个应用程序将具有相同的 classes,但它们的处理方式仍然不同。所以 class 装载机来了。
所以你可以这样想 Tomcat 为每个应用程序创建一个 class 加载器并在其下加载它们。
加载器的回收:上面如果Tomcat持有对加载器对象的引用,那么加载器对象将不会被回收。除非加载程序被垃圾收集,否则它加载的 classes 会保留。
因此,如果您关闭一个应用程序,Tomcat 最终将重新调用它各自的加载程序,以便 gc 可以回收它并清理它,包括由它加载的 classed。
可能有帮助的快速链接:
.
让我们看看这段代码以了解类加载器泄漏是如何发生的
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 持有对象列表的引用,因为类加载器加载的实例保存在其中。
现在,让我们稍微更改一下代码,所以在这里我们将列表设置为 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 完成!
很抱歉问你这个基本问题,但我无法理解这个概念。 我读了很多 SO post 但我无法理解。能否请你给我代码示例来理解。
- 如本文所述post
Static variables cannot be elected for garbage collection while the class is loaded. They can be collected when the respective class loader (that was responsible for loading this class) is itself collected for garbage.
理论上我理解如果有引用就不能收集Classloader,但我不明白这在实践中是如何实现的。 能否请您用代码示例进行解释?
非常感谢您的帮助!
发表我的理解希望对你有帮助, 背景理解: 理解这一点的简单方法是以 Tomcat 或任何此类应用程序为例。这是基于 java 的。 Tomcat 可以 运行 多个网络应用程序。即使您使用不同的名称部署相同的应用程序,它们也会受到不同的对待。在这里,这两个应用程序将具有相同的 classes,但它们的处理方式仍然不同。所以 class 装载机来了。 所以你可以这样想 Tomcat 为每个应用程序创建一个 class 加载器并在其下加载它们。
加载器的回收:上面如果Tomcat持有对加载器对象的引用,那么加载器对象将不会被回收。除非加载程序被垃圾收集,否则它加载的 classes 会保留。 因此,如果您关闭一个应用程序,Tomcat 最终将重新调用它各自的加载程序,以便 gc 可以回收它并清理它,包括由它加载的 classed。
可能有帮助的快速链接: .
让我们看看这段代码以了解类加载器泄漏是如何发生的
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 持有对象列表的引用,因为类加载器加载的实例保存在其中。
现在,让我们稍微更改一下代码,所以在这里我们将列表设置为 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 完成!