Spring + MongoDB:潜在的内存泄漏消息

时间:2014-06-24 15:14:08

标签: java spring mongodb memory-leaks

今天我尝试在我的网络应用程序中修复一些潜在的内存泄漏。

我使用以下库。

  • 弹簧webmvc-3.2.9.RELEASE
  • 弹簧数据的mongodb-1.5.0.RELEASE
  • 蒙戈-java的驾驶员2.12.1

首先我错过了关闭MongoClient但是改变了我的配置。

@Configuration
public class MongoDBConfiguration implements DisposableBean {
    private MongoClient mongoClient;

    @Bean
    public MongoTemplate mongoTemplate() {
        try {
            final Properties props = loadProperties();
            log.debug("Initializing Mongo DB client");
            mongoClient =
                    new MongoClient(getProperty(props, "host", "localhost"), cint(getProperty(props, "port",
                            "27017")));
            UserCredentials credentials = null;
            final String auth = getProperty(props, "auth", null);
            if (auth != null && auth.equalsIgnoreCase("true")) {
                final String user = getProperty(props, "user", null);
                final String pass = getProperty(props, "pass", null);
                if (user != null && pass != null) {
                    credentials = new UserCredentials(user, pass);
                }
            }
            final MongoDbFactory mongoDbFactory =
                    new SimpleMongoDbFactory(mongoClient, getProperty(props, "dbname", "Feeder"), credentials);
            final MappingMongoConverter mongoConverter =
                    new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory),
                            new MongoMappingContext());
            mongoConverter.setCustomConversions(customConversions(mongoConverter));
            mongoConverter.afterPropertiesSet();
            final MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, mongoConverter);
            return mongoTemplate;
        } catch (final IOException e) {
            log.error("", e);
        }
        return null;
    }

    /**
     * close Mongo client to avoid memory leaks
     */
    @Override
    public void destroy() {
        log.debug("Shutdown Mongo DB connection");
        mongoClient.close();
        log.debug("Mongo DB connection shutdown completed");
    }
}

当停止或重新加载Web应用程序时,仍有消息抱怨可能的内存泄漏。

2014-06-24 07:58:02,114 DEBUG d.p.f.s.m.MongoDBConfiguration - Shutdown Mongo DB connection
2014-06-24 07:58:02,118 DEBUG d.p.f.s.m.MongoDBConfiguration - Mongo DB connection shutdown completed
Jun 24, 2014 7:58:02 AM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks
SEVERE: The web application [/feeder##1.5.1] created a ThreadLocal with key of type [com.mongodb.BaseCluster$1] (value [com.mongodb.BaseCluster$1@766465]) and a value of type [java.util.Random] (value [java.util.Random@5cb9231f]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

第3行和第4行重复最多9次,就像我看到的那样。

我该如何解决这个问题?可以忽略吗?

1 个答案:

答案 0 :(得分:0)

您需要清理线程本地。请在此处查看我的回答stop/interrupt a thread after jdbc Driver has been deregister

/**
 * Cleanup function which cleans all thread local variables. Using thread
 * local variables is not a good practice but unfortunately some libraries
 * are still using them. We need to clean them up to prevent memory leaks.
 * 
 * @return number of Thread local variables
 */
private int immolate() {
    int count = 0;
    try {
        final Field threadLocalsField = Thread.class
                .getDeclaredField("threadLocals");
        threadLocalsField.setAccessible(true);
        final Field inheritableThreadLocalsField = Thread.class
                .getDeclaredField("inheritableThreadLocals");
        inheritableThreadLocalsField.setAccessible(true);
        for (final Thread thread : Thread.getAllStackTraces().keySet()) {
            count += clear(threadLocalsField.get(thread));
            count += clear(inheritableThreadLocalsField.get(thread));
        }
        log.info("Immolated " + count + " values in ThreadLocals");
    } catch (Exception e) {
        log.error("ThreadLocalImmolater.immolate()", e);
    }
    return count;
}

/**
 * Cleaner for thread local map.
 * 
 * @param threadLocalMap
 *            thread local map to clean or null
 * @return number of cleaned objects
 * @throws Exception
 *             in case of error
 */
private int clear(@NotNull final Object threadLocalMap) throws Exception {
    if (threadLocalMap == null) {
        return 0;
    }
    int count = 0;
    final Field tableField = threadLocalMap.getClass().getDeclaredField(
            "table");
    tableField.setAccessible(true);
    final Object table = tableField.get(threadLocalMap);
    for (int i = 0, length = Array.getLength(table); i < length; ++i) {
        final Object entry = Array.get(table, i);
        if (entry != null) {
            final Object threadLocal = ((WeakReference<?>) entry).get();
            if (threadLocal != null) {
                log(i, threadLocal);
                Array.set(table, i, null);
                ++count;
            }
        }
    }
    return count;
}