Java Spring应用程序有内存泄漏。系统非堆内存不断增加

时间:2016-10-14 09:56:45

标签: java spring memory-leaks

我使用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) {

                            }

                        }
                    }

                }
            }
        }
    }

}

关于内存泄漏监视器的快照如下所示。

enter link description here

2 个答案:

答案 0 :(得分:1)

there is an answer here

  

您所看到的是一种症状,而不是您的泄漏原因。   泄漏的原因是从未收集过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上最相关的包,它对你有帮助