我使用yourkit profiler监控My Web应用程序。保留最大大小的主要对象是SessionFactoryImpl,webappclassloader和CGlib对象显示。 * Spring crone调度程序会导致内存泄漏吗? 我试过的解决方案
1)我试图杀死线程,但他们还活着。
2)关闭所有连接。
3)为我在代码中使用的所有变量和对象分配null。
4)我也应用了serveride
-Xms128m -Xmx256m -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:MaxHeapFreeRatio=70 -XX:ReservedCodeCacheSize=32m -XX:+UseCodeCacheFlushing -XX:-OmitStackTraceInFastThrow
5)我在web.xml中添加了防漏库
<listener>
<listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorListener
</listener-class>
</listener>
<context-param>
<param-name>ClassLoaderLeakPreventor.stopThreads</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>ClassLoaderLeakPreventor.stopTimerThreads</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>ClassLoaderLeakPreventor.executeShutdownHooks</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>ClassLoaderLeakPreventor.threadWaitMs</param-name>
<param-value>5000</param-value>
</context-param>
<context-param>
<param-name>ClassLoaderLeakPreventor.shutdownHookWaitMs</param-name>
<param-value>10000</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
6)我还添加了ContextFinalizer类
package com.thl.test;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;
import java.util.Set;
import javax.servlet.ServletContextEvent;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.CachedIntrospectionResults;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.util.IntrospectorCleanupListener;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
public class ContextFinalizer extends IntrospectorCleanupListener {
private ClassLoader loader = null;
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Calling>>>>>>>>>>>>>>>>>>>>>>>.?");
/* Introspector.flushCaches(); */
ClassLoader cl1 = Thread.currentThread().getContextClassLoader();
CachedIntrospectionResults.clearClassLoader(cl1);
LogFactory.releaseAll();
ClassLoaderLeakPreventor.gc();
try {
AbandonedConnectionCleanupThread.shutdown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*Enhancer.registerCallbacks(enhanced, null);*/
// cleanUp();
}
@SuppressWarnings("deprecation")
public void contextDestroyed(ServletContextEvent sce) {
/*
* Thread t = Thread.currentThread();
* Runtime.getRuntime().addShutdownHook(t);
*/
System.out.println("Good Bye>>>>>>>>>>>>>>>>>>>>>.?");
cleanUp();
ClassLoaderLeakPreventor.gc();
java.beans.Introspector.flushCaches();
java.security.Security.removeProvider(null);
ClassLoader cl1 = Thread.currentThread().getContextClassLoader();
CachedIntrospectionResults.clearClassLoader(cl1);
LogFactory.releaseAll();
org.apache.log4j.LogManager.shutdown();
Enumeration<Driver> drivers = DriverManager.getDrivers();
Driver d = null;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
while (drivers.hasMoreElements()) {
try {
d = drivers.nextElement();
if (d.getClass().getClassLoader() == cl) {
DriverManager.deregisterDriver(d);
} else {
DriverManager.deregisterDriver(d);
}
} catch (Exception ex) {
// LOGGER.warn(String.format("Error deregistering driver %s",
// d), ex);
}
}
/*
* if (ConnectionImpl.class.getClassLoader() ==
* getClass().getClassLoader()) { Field f = null; try { f =
* ConnectionImpl.class.getDeclaredField("cancelTimer");
* f.setAccessible(true); Timer timer = (Timer) f.get(null);
* timer.cancel(); }catch(Exception e) {
*
* }finally { f = null; } }
*/
try {
com.mysql.jdbc.AbandonedConnectionCleanupThread.shutdown();
} catch (InterruptedException e) {
} finally {
try {
/* org.apache.commons.pool.impl.GenericObjectPool. */
com.mysql.jdbc.AbandonedConnectionCleanupThread.shutdown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for (Thread t : threadArray) {
/*
* if (t.isInterrupted()) { break; }
*/
if (t.getName().contains("Abandoned connection cleanup thread")) {
synchronized (t) {
// don't complain, it works
if (t.isAlive()) {
System.out.println("Alive True");
if (t.isDaemon()) {
System.out.println("isDaemon True");
t.stop();
} else {
System.out.println("isDaemon False");
t.stop();
}
} else {
System.out.println("Alive Flase");
t.stop();
}
// new Timer(true);
}
} else if (t.getName().contains("http-nio-8081-exec-1")) {
System.out.println("http-nio-8081-exec-1>>>>>>>>>>>");
} else {
System.out.println("Else If Block");
synchronized (t) {
t.setDaemon(true);
t.suspend();
}
}
}
java.beans.Introspector.flushCaches();
}
public void onApplicationEvent(ContextRefreshedEvent arg0) {
System.out.println("--------------- Context Refreshed -----------------");
System.out.println(":::::::::::::::::::::::: Calling :::::::::::::::::::::::::::::");
ApplicationContext context = arg0.getApplicationContext();
System.out.println(context.getDisplayName());
}
private void cleanUp() {
Thread[] threads = getThreads();
for (Thread thread : threads) {
if (thread != null) {
System.out.println("Inside IFF");
cleanContextClassLoader(thread);
cleanOrb(thread);
cleanThreadLocal(thread);
}
}
}
private Thread[] getThreads() {
ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parentGroup;
if (rootGroup.getParent() != null) {
parentGroup = rootGroup.getParent();
if (parentGroup != null) {
rootGroup = parentGroup;
}
}
Thread[] threads = new Thread[rootGroup.activeCount()];
while (rootGroup.enumerate(threads, true) == threads.length) {
threads = new Thread[threads.length * 2];
}
return threads;
}
private boolean loaderRemovable(ClassLoader cl) {
if (cl == null) {
return false;
}
Object isDoneCalled = getObject(cl, "doneCalled");
String clName = cl.getClass().getName();
loader = Thread.currentThread().getContextClassLoader();
String ldr = null;
loader = loader.getParent();
if (loader != null) {
// loader.getParent();
ldr = loader.getClass().getName();
}
if (clName != null && ldr != null && isDoneCalled != null) {
if (clName.equalsIgnoreCase(ldr) && isDoneCalled instanceof Boolean && (Boolean) isDoneCalled) {
return true;
}
}
return loader == cl;
}
private Field getField(Class clazz, String fieldName) {
Field f = null;
try {
f = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException ex) {
} catch (SecurityException ex) {
}
if (f == null) {
Class parent = clazz.getSuperclass();
if (parent != null) {
f = getField(parent, fieldName);
}
}
if (f != null) {
f.setAccessible(true);
}
return f;
}
private Object getObject(Object instance, String fieldName) {
Class clazz = instance.getClass();
Field f = getField(clazz, fieldName);
if (f != null) {
try {
return f.get(instance);
} catch (IllegalArgumentException | IllegalAccessException ex) {
}
}
return null;
}
private void cleanContextClassLoader(Thread thread) {
if (loaderRemovable(thread.getContextClassLoader())) {
thread.setContextClassLoader(null);
}
}
private void cleanOrb(Thread thread) {
Object currentWork = getObject(thread, "currentWork");
if (currentWork != null) {
Object orb = getObject(currentWork, "orb");
if (orb != null) {
Object transportManager = getObject(orb, "transportManager");
if (transportManager != null) {
Thread selector = (Thread) getObject(transportManager, "selector");
if (selector != null && loaderRemovable(selector.getContextClassLoader())) {
selector.setContextClassLoader(null);
}
}
}
}
}
private void removeThreadLocal(Object entry, Object threadLocals, Thread thread) {
ThreadLocal threadLocal = (ThreadLocal) getObject(entry, "referent");
if (threadLocal != null) {
Class clazz = null;
try {
clazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
} catch (ClassNotFoundException ex) {
}
if (clazz != null) {
Method removeMethod = null;
Method[] methods = clazz.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
if (method.getName().equals("remove")) {
removeMethod = method;
removeMethod.setAccessible(true);
break;
}
}
}
if (removeMethod != null) {
try {
removeMethod.invoke(threadLocals, threadLocal);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
}
}
}
}
}
private void cleanThreadLocal(Thread thread) {
Object threadLocals = getObject(thread, "threadLocals");
if (threadLocals != null) {
Object table = getObject(threadLocals, "table");
if (table != null) {
int size = Array.getLength(table);
for (int i = 0; i < size; i++) {
Object entry = Array.get(table, i);
if (entry != null) {
Field valueField = getField(entry.getClass(), "value");
if (valueField != null) {
try {
Object value = valueField.get(entry);
if (value != null && value instanceof ClassLoader
&& loaderRemovable((ClassLoader) value)) {
removeThreadLocal(entry, threadLocals, thread);
}
} catch (IllegalArgumentException | IllegalAccessException ex) {
}
}
}
}
}
}
}
}
关于内存泄漏监视器的快照如下所示。
答案 0 :(得分:1)
您所看到的是一种症状,而不是您的泄漏原因。 泄漏的原因是从未收集过ClassLoader CachedIntrospectionResults在哪种情况下效果不大 比较每个类所占用的内存及其静态 成员。如果没有收集您的ClassLoader,您甚至可能会收到 没有释放外部资源,例如JDBC Connections,如果你有 从静态成员引用到您的连接池(直接或 间接地)。
任何servlet容器都会在webapp发布时释放ClassLoader 卸载,但有很多东西(错误)可能会阻止 要收集的ClassLoader。我个人最可能的原因 能够识别java.lang.Introspector中的缓存,一个驱动程序 未在DriverManager或ThreadLocal变量中未注册 清除。但无论如何,除非补丁中有错误我 提供,或者它以某种方式被破坏(在这两种情况下我都怀疑 它),然后CacheIntrospectionResults中的代码永远不会阻止 ClassLoader被垃圾收集。
所以,很可能,它不是JBoss问题,也不是Spring问题,而是a 您自己的代码或DOM4J等库中的问题。
对于Introspector,解决方案(除了不使用它之外)就是拥有一个 ContextListener在上下文中调用Introspector.flushCaches() 被毁了。春天提供一个,它的 os.web.util.IntrospectorCleanupContextListener或类似的东西。 此外,Introspector中的泄漏只是JDK到JDK的问题 1.4.2,问题在1.5中得到纠正。
对于未注册的驱动程序(如果您的驱动程序类在 WEB-INF / lib),解决方案类似,你需要有一个 ContextListener,当上下文被销毁时,你要求列表 从DriverManager注册的驱动程序,你删除任何 来自您的webapp(您可以检查其ClassLoader)。
对于ThreadLocal的东西,你必须确保放入任何东西 在那里你最终删除它。但是,如果第三方图书馆这样做 (例如DOM4J)您无法修复它或修复它,那么它就更多了 很难解决。您可以查看我以前的电子邮件 一个pontential解决方法的列表,但它是一种黑客,并且会 由于并发问题而在生产中存在危险
答案 1 :(得分:0)
如果您认为您的弹簧代码有问题,请在jvisualvm上过滤您的包裹,然后您可以看到您的实例记忆。
在我看来,我认为你没有正确使用和配置其他库,它可能会对你的应用程序造成一些内存问题。让我们检查jvisualvm上最相关的包,它对你有帮助