Luk*_*lin 6 java tomcat memory-leaks jprofiler
我正在调试我在Tomcat应用程序中使用多年的问题 - 重新启动应用程序时导致的内存泄漏,因为Webapp类加载器无法进行GC.我用JProfiler拍摄堆的快照,似乎至少有一些我的静态变量没有被释放.
某些类具有静态最终成员,该成员在首次加载类时初始化,并且因为它是最终的,所以我无法在应用程序关闭时将其设置为null.
静态最终变量是Tomcat中的反模式,还是我错过了什么?我刚刚开始使用JProfiler 8,所以我可能误解了传入的引用告诉我的内容.
干杯!
卢克
当类本身被垃圾回收时,静态变量应该被垃圾回收,反过来,当它的类加载器被垃圾回收时也是如此。
您可以通过让应用程序类加载器未加载的任何内容引用您的任何类(或类的实例)来轻松创建内存泄漏。寻找您没有正确删除的回调侦听器等(内部/匿名类很容易被忽略)。
对您的一个类的单一引用会阻止其类加载器,进而阻止该类加载器加载的任何类被垃圾收集。
编辑,泄漏防止所有类的 GC 的对象的示例:
MemoryMXBean mx = ManagementFactory.getMemoryMXBean();
NotificationListener nl = new NotificationListener() { ... };
((NotificationEmitter) mx).addNotificationListener(nl, ..., ...);
Run Code Online (Sandbox Code Playgroud)
如果您使用存在于应用程序范围之外的对象(此处为 MemoryMXBean)注册一个侦听器(此处为 NotificationListener),则您的侦听器将保持“活动”状态,直到它被明确删除。由于您的侦听器实例持有对其 ClassLoader(您的应用程序类加载器)的引用,您现在已经创建了一个强大的引用链,以防止类加载器的 GC,进而阻止它加载的所有类,以及这些类持有的任何静态变量。
Edit2:基本上你需要避免这种情况:
[Root ClassLoader]
|
v
[Application ClassLoader]
|
v
(Type loaded by Root).addSomething()
Run Code Online (Sandbox Code Playgroud)
运行应用服务器的 JVM 已经通过根类加载器(也可能是应用服务器)加载了 JRE。这意味着这些类永远不会有资格进行 GC,因为总会有对其中一些类的实时引用。应用程序服务器将在一个单独的类加载器中加载您的应用程序,当您的应用程序被重新部署(或至少应该)时,它将不再持有引用。但是您的应用程序将与应用程序服务器(至少是 JRE,但通常还有应用程序服务器)共享至少来自 JRE 的所有类。
在假设的情况下,当应用程序服务器要创建一个单独的类加载器(没有父类,实际上是第二个根类加载器)并尝试第二次加载 JRE(对于您的应用程序来说是私有的)时,它会导致很多问题. 打算成为单例的类将存在两次,并且两个类层次结构将无法保存另一个类的任何引用(由不同的类加载器加载的同一个类为 JVM 提供不同的类型)。他们甚至不能使用 java.lang.Object 作为各自“其他”类加载器对象的引用类型。
它是几年前的,但我在JavaOne 上的演示完全涵盖了这个主题.找到泄漏的关键步骤在幻灯片11中,但是有很多背景信息也可能有用.
简短版本是:
正如我在演示文稿中指出的那样,找到漏洞是一回事,找到触发它们的东西可能会困难得多.
我建议运行最新的稳定Tomcat版本,因为我们总是在改进内存泄漏检测和防范代码,并且生成的警告和错误也可能提供一些指示.
| 归档时间: |
|
| 查看次数: |
3013 次 |
| 最近记录: |