关于 JVM Class被回收卸载的问题?

问题的背景是我看到了这篇文章:
https://zhuanlan.zhihu.com/p/146410261
提到

强引用链: thread -> threadLocalMap -> counter -> MyCounter.class -> WebappClassLocader ,导致WebappClassLoader泄漏。

对这个有疑问。

Class类型被卸载的条件

图片

疑问点是

其中第二点: 加载该类的类加载被回收,是指 类加载器对象被gc。

假设MyCounter.class 由 WebAppClassLoader加载,那么 通过MyCounter.class.getClassLoader() 就可以得到WebAppClassLoader, 而WebAppClalssLoader 被gc的条件必然是没有任何人引用她,

所以 如何做到MyCounter.class 不会引用WebAppClassLoader? 否则是不是 就会出现 WebAppClassLoader一直被MyCounter.class所引用呢?

如果一直被引用的话那么WebAppClassLoader对象就不会被gc,那么MyCounter.class对象自然也不会被gc了。

感觉是鸡生蛋蛋生鸡的问题。

如果说 MyCounter.class 对 WebAppClassLoader对象有强引用, WebAppClassLoader对象又怎么可能会被回收呢。如果WebAppClassLoader对象不被gc,那么MyCOunter.class又怎么会被回收呢?

b

阅读 965
avatarAI BotBETA

关于你的问题,首先需要明确的是,Java中的类是一种特殊的对象,是由类加载器(ClassLoader)加载的。类加载器在加载类时,会创建一个代表这个类的Class对象。这个Class对象可以通过类名.class或者getClassLoader()等方法获取。

在你的问题中,你提到了一种情况,即一个类(MyCounter.class)通过类加载器(WebAppClassLoader)加载,然后这个类和类加载器之间存在引用关系。你担心的是,如果WebAppClassLoader被垃圾回收,那么MyCounter.class也会被回收,因为MyCounter.class引用了WebAppClassLoader。

实际上,这种担心是不必要的。在Java中,当一个对象没有任何其他对象引用它时,它就会被垃圾回收。也就是说,只有当没有任何其他对象引用WebAppClassLoader时,它才会被垃圾回收。

对于你的疑问,"如何做到MyCounter.class不会引用WebAppClassLoader",实际上是不可能的。因为MyCounter.class是由WebAppClassLoader加载的,所以它必然会引用WebAppClassLoader。但是这并不意味着WebAppClassLoader不能被垃圾回收。当WebAppClassLoader不再被其他任何对象引用时,它就会被垃圾回收,从而使得MyCounter.class失去引用,进而可以被垃圾回收。

所以,"鸡生蛋蛋生鸡"的问题实际上并不存在。在Java中,只有当没有任何其他对象引用一个对象时,该对象才会被垃圾回收。如果有其他对象引用该对象,那么这个对象就不会被垃圾回收,即使它没有被任何其他对象引用。

最后,关于你的问题,"如果说 MyCounter.class 对 WebAppClassLoader对象有强引用,WebAppClassLoader对象又怎么可能会被回收呢",实际上是理解有误。在Java中,一个对象是否被垃圾回收,并不是由它是否有其他对象引用它来决定的,而是由它是否被任何其他对象引用以及垃圾回收器的状态来决定的。如果一个对象不再被任何其他对象引用,并且垃圾回收器决定回收这个对象,那么这个对象就会被垃圾回收。

2 个回答

我梳理了一下,你现在的问题不是在探讨为什么threadLocal在tomcat中造成了内存泄露,而是只考虑类加载器与类的互引用让你觉得它们无法被卸载对吧

"WebAppClalssLoader 被gc的条件必然是没有任何人引用"
这是准确的吗?假设内存中有一对相互引用的对象,但是他们都没有任何其他的对象所引用,难道JVM就无法回收他们吗?
在不同的GC算法里对这种情况应该都是有所处理的,比如标记清除算法等等,尽管有相互引用,但是由于没有从根对象到达它们的路径,所以还是有可能被回收的。这是回答你的问题。
另外,这篇知乎文章的观点我也不是很认同。使用threadLocal时必须注意线程在threadLocal内持有的对象是否能回收。他的情况是认为通过tomcat对web应用进行reload之后就可以不管不顾了,殊不知他的程序根本没有正确地结束所有的线程,进而线程在threadLocal内持有的对象无法被回收,进而导致整个应用无法被回收。他的内存泄露是reload时不能彻底关闭之前的应用导致的

新手上路,请多包涵

可以了解下可达性算法的分析:GCROOT和三色标记法

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题