Skip to the content.

[Java][JVM][Memory leak][Profiling][Spring] Spring Session JDBC + Apereo CAS = Heap memory leak, and it is a documented feature

Before you read this article

Make sure you have read my previous article about a Spring + CAS memory leak.

How a CAS client removes a HTTP Session from its storage

A little reminder for you, the CAS client removes a HTTP Session using an implementation below:

public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
    ...
    @Override
    public void sessionDestroyed(final HttpSessionEvent event) {
        if (sessionMappingStorage == null) {
            sessionMappingStorage = getSessionMappingStorage();
        }
        final HttpSession session = event.getSession();
        sessionMappingStorage.removeBySessionById(session.getId());
    }
    ...
}

It is also mentioned in the Javadoc of that class.

Mind that HttpSessionListener class is a part of HTTP server, not a Spring Framework. If you register such a listener then during the session invalidation Embedded Tomcat at Spring is going to emit an event of type javax.servlet.http.HttpSessionEvent. Such an event is processed by all the listeners of type HttpSessionListener.

Spring documentation

As I wrote in the title of that article, all the behavior described below were documented. Let’s look at the Spring Session documentation:

Spring Session supports HttpSessionListener by translating SessionDestroyedEvent and SessionCreatedEvent into HttpSessionEvent by declaring SessionEventHttpSessionListenerAdapter. To use this support, you need to:

The SessionRepository used in JDBC implementation of Spring Session is defined in JdbcIndexedSessionRepository. Let’s look at the Javadoc:

A SessionRepository implementation that uses Spring’s JdbcOperations to store sessions in a relational database. This implementation does not support publishing of session events.

Why is there a leak?

Long story short:

And yes, it is all documented, so RTFM :)

What can we do about it?

The most proper way of dealing with this memory leak is:

How can we hack it?

I don’t see any good and easy way of hacking the implementation. We need to be prepared for two actions:

The implementation of removing expired session is done by simple JDBC delete:

public void cleanUpExpiredSessions() {
    Integer deletedCount = this.transactionOperations
            .execute((status) -> JdbcIndexedSessionRepository.this.jdbcOperations.update(
                    JdbcIndexedSessionRepository.this.
                    deleteSessionsByExpiryTimeQuery, System.currentTimeMillis()));

    if (logger.isDebugEnabled()) {
        logger.debug("Cleaned up " + deletedCount + " expired sessions");
    }
}

Possible hacks that I can think of:

What will be done in the “original” application, where I’ve found that leak?

Nothing :) Yes, there is a memory leak, but this leak in that application is not dangerous. The memory leaks about 100MB/month with a 3GB heap and deployments are done at least once a month. The size of that leak depends on the number of unique sessions (created with login through CAS) in the application. If that number increases to the level in which that leak becomes dangerous, the profiled application is going to be switched to another implementation of Spring Session.

The most important part is that the team developing the application knows the reason behind the leak and can easily remove it when necessary. For now adding new technology (Hazelcast/Redis) is not cost-effective.