Java 平台企业修订版应用程序中的内存泄漏
内存泄漏有各种类型,例如,线程和 ThreadLocal 泄漏、ClassLoader 泄漏、系统资源泄漏和连接泄漏。内存泄漏的检测方法通常包括检查 Java™ 虚拟机工具接口 (JVMTI) 或性能监视基础结构 (PMI) 计数器,以观察 Java 或本机堆使用率的缓慢增长。
注: WebSphere® Application Server V8.5
提供了基于自顶向下模式的内存泄漏检测、预防和操作(通过在运行时监视应用程序代码内的可疑模式)。WebSphere Application
Server 提供了一些方法,以在停止或重新部署应用程序时预防内存泄漏。如果启用了泄漏检测、预防和操作,那么 WebSphere Application
Server 会监视应用程序和模块活动并在某个应用程序或个别模块停止时执行诊断操作以检测和修正泄漏。此功能有助于通过经常进行应用程序重新部署来增加应用程序正常运行时间,而无需重启服务器。
类装入器内存泄漏
许多内存泄漏会将它们自己列为类装入器泄漏。Java 类由其名称及装入它的类装入器唯一标识。可在单个 JVM 中多次装入同名类,每个类在不同类装入器中。每个 Web 应用程序获取自己的类装入器,WebSphere Application Server 使用类装入器来隔离应用程序。
对象保留对它作为其实例的类的引用。类保留对装入它的类装入器的引用。类装入器保留对它装入的每个类的引用。保留对来自 Web 应用程序的单个对象的引用会固定 Web 应用程序装入的每个类。Web 应用程序重新装入后,这些引用通常会保留。对于每次重新装入,都会固定更多类,这会导致内存不足错误。
类装入器内存泄漏通常由应用程序代码或 JRE 触发代码导致。
JRE 触发泄漏
Java 运行时环境 (JRE) 代码使用上下文类装入器来装入应用程序单项时,会发生内存泄漏。这些单项可以是 JRE 使用上下文类装入器装入的线程或其他对象。
如果 Web 应用程序代码触发单项或静态初始化程序的初始化,那么会出现以下情况:
- 上下文类装入器变为 Web 应用程序类装入器。
- 系统会创建对 Web 应用程序类装入器的引用。系统决不会对此引用执行垃圾回收操作。
- 在内存中固定类装入器及它装入的所有类。
应用程序触发泄漏
应用程序触发泄漏的类别如下所示:
- 定制 ThreadLocal 类
- Web 应用程序类实例作为 ThreadLocal 值
- 通过 ThreadLocal 值间接持有 Web 应用程序类实例
- ThreadLocal 伪泄漏
- Web 应用程序创建的 ContextClassLoader 和线程
- 常规类装入器装入的类创建的 ContextClassLoader 和线程
- 静态类变量
- JDBC 驱动程序注册:RMI 目标
WebSphere Application Server 现在提供了一些方法,以在停止或重新部署应用程序时预防内存泄漏。WebSphere Application Server 会监视应用程序和模块活动,并在某个应用程序或个别模块停止时执行诊断操作。
在 WebSphere Application Server 中,此内存泄漏功能部件包含三个部分:检测、预防和操作。
- 检测:检测到内存泄漏时发出警告。通过将标准 API 调用与 Web 应用程序停止、取消部署或重新装入时的一些反射技术结合使用。检测到应用程序泄漏时,WebSphere Application Server 会检查内存泄漏的已知原因并发出警告,如下所示:
[11/17/11 12:01:05:911 EST] 00000005 LeakDetection E CWMML0015E: The web application [WasSwat#WasSwatWeb.war] created a ThreadLocal with key of type [test.memleak.MyThreadLocal] (value [test.memleak.MyThreadLocal@216c691]) and a value of type [test.memleak.MyCounter] (value [test.memleak.MyCounter@21942ff]) but failed to remove it when the web application was stopped.
- 缺省情况下,预防处于启用状态,并且仅应用于 JRE 触发泄漏。如果应用程序服务器类装入器是上下文类装入器,那么可通过在服务器启动时初始化单项来预防 JRE 触发泄漏。
- 操作:采取积极措施来修正内存泄漏。这些措施都有合理缺省值并且是逐个案例配置的。
protected void com.ibm.ws.classloader.clearReferences(){ if(ENABLE_CLEAR_REFERENCES_JDBC) clearReferencesJdbc(); if(ENABLE_CLEAR_REFERENCES_THREADS) clearReferencesThreads(); if(ENABLE_CLEAR_REFERENCES_THREADLOCALS) clearReferencesThreadLocals(); if(ENABLE_CLEAR_REFERENCES_RMI_TARGETS) clearReferencesRmiTargets(); if(ENABLE_CLEAR_REFERENCES_STATICS) clearReferencesStaticFinal(); }
泄漏原因 | 如何修正 | 要启用和控制的 WebSphere Application Server Java 虚拟机属性 |
---|---|---|
Threadlocal | 在可配置时间段更新线程池中的线程。从池中获取线程允许对线程和 threadlocals 进行垃圾收集。 |
|
HttpClient 保持活动线程 | 将线程切换到父类装入程序。 |
|
计时器线程 | 使用反射来停止可能安排的任何新任务。 |
|
非 JVM 控制线程 | 如果线程是使用执行程序启动的,请关闭执行程序或中断该线程。 |
|
JDBC 驱动程序 | 注销 Web 应用程序已注册但已遗忘的 JDBC 驱动程序。 |
|
ResourceBundle | 清除此类装入程序或此装入程序充当父类装入程序的任何类装入程序所装入的所有捆绑软件的 ResourceBundle 高速缓存。 |
|
RMI 目标 | 使用反射清除 sun.rmi.transport.ObjectTable.implTable 和 sun.rmi.transport.ObjectTable.objTable 中的值。 |
|
静态类变量 | WebSphere Application Server 会使应用程序或模块类装入程序装入的类的所有静态类变量的值为空。 |
|