简体   繁体   中英

Spring + MongoDB: potential memory leak messages

Today I tried to fix some potential memory leaks in my web application.

I use the following libraries.

  • spring-webmvc-3.2.9.RELEASE
  • spring-data-mongodb-1.5.0.RELEASE
  • mongo-java-driver-2.12.1

First I missed to close the MongoClient but changed my configuration this way.

@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");
    }
}

When stopping or reloading the web application, there are still messages complaining about probable memory leaks.

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.

Lines 3 and 4 are repeated up to 9 times, as fare as I have seen.

How can I fix this? Can it be ignored?

You need to clean thread locals. See my answer here 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM