Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActivityCorrelator is causing a classloader leak #314

Closed
JohnA2 opened this issue May 25, 2017 · 4 comments
Closed

ActivityCorrelator is causing a classloader leak #314

JohnA2 opened this issue May 25, 2017 · 4 comments
Assignees

Comments

@JohnA2
Copy link

JohnA2 commented May 25, 2017

ActivityCorrelator uses ThreadLocal, but it never calls ThreadLocal.remove() to clean it up. As a result, if a thread pool is used, the classloader that loaded ActivityCorrelator can never be garbage collected thus creating a memory leak.

Example of the path from GC root:

this     - value: org.apache.catalina.loader.WebappClassLoader #3
 <- <classLoader>     - class: com.microsoft.sqlserver.jdbc.ActivityId, value: org.apache.catalina.loader.WebappClassLoader #3
  <- <class>     - class: com.microsoft.sqlserver.jdbc.ActivityId, value: com.microsoft.sqlserver.jdbc.ActivityId class ActivityId
   <- value     - class: java.lang.ThreadLocal$ThreadLocalMap$Entry, value: com.microsoft.sqlserver.jdbc.ActivityId #2
    <- [54]     - class: java.lang.ThreadLocal$ThreadLocalMap$Entry[], value: java.lang.ThreadLocal$ThreadLocalMap$Entry #137
     <- table     - class: java.lang.ThreadLocal$ThreadLocalMap, value: java.lang.ThreadLocal$ThreadLocalMap$Entry[] #28
      <- threadLocals (thread object)     - class: org.apache.tomcat.util.threads.TaskThread, value: java.lang.ThreadLocal$ThreadLocalMap #28
@v-nisidh v-nisidh added this to the Long Term Goals milestone May 30, 2017
v-nisidh added a commit to v-nisidh/mssql-jdbc that referenced this issue May 31, 2017
@v-nisidh v-nisidh self-assigned this May 31, 2017
@v-nisidh
Copy link
Contributor

@JohnA2 : Can you check if #321 PR prevents class loader leak?

If it's not fixed then let me know.

@v-nisidh
Copy link
Contributor

v-nisidh commented Jun 1, 2017

@JohnA2 : I able to detect leakage in with my patch so I am closing PR right now. After my fixes I will open same or new PR.

Thanks @JohnA2 for opening this issue.

@JohnA2
Copy link
Author

JohnA2 commented Jun 1, 2017

Thanks for the quick fix, @v-nisidh! The leak in my particular case is fixed, because I don't use a connection pool for the task I'm using mssql-jdbc for. I use Spring Framework's JdbcTemplate to open a new connection, execute a single SQL query and close the connection. All this happens in the same thread, so the current fix works just fine.

However, as @marschall mentioned in #321, this fix won't work for a more traditional case with a web application running on an application server that uses a connection pool, because different threads can be used to open and close the same connection, and ThreadLocal.remove() doesn't have an effect as it can only clean up ThreadLocal for the thread it's called from.

Here's the source code for ThreadLocal.remove()

public void remove() {
	ThreadLocalMap m = getMap(Thread.currentThread());
	if (m != null)
		m.remove(this);
}

As you can see it uses the ThreadLocalMap from the current thread, so there's no easy way to clean up another thread's ThreadLocal. I would suggest considering using InheritableThreadLocal instead of ThreadLocal to avoid this limitation or even better not use ThreadLocals at all, if possible.

@marschall You are correct that it's recommended to deploy JDBC drivers to the application server itself, but it's only because of the way DriverManager works, see https://stackoverflow.com/questions/6981564/why-must-the-jdbc-driver-be-put-in-tomcat-home-lib-folder. But it's no longer a problem if you unregister the driver from DriverManager during ServletContext shutdown. There are some cases when it's useful because it's undesirable or impossible to modify the application server's configuration. However leaky JDBC drivers create a problem in this scenario. The leak still exists when the driver is deployed on the application server level, it's just hidden because the driver classes are loaded by the app server's own classloader, which is never supposed to be garbage collected anyway. Other JDBC drivers I used - Oracle and MySQL don't have a leak.

@v-nisidh v-nisidh added Work in Progress The pull request is a work in progress and removed PR Under Review labels Jun 5, 2017
@v-nisidh v-nisidh added PR Under Review and removed Work in Progress The pull request is a work in progress labels Jul 6, 2017
@peterbae peterbae assigned peterbae and unassigned v-nisidh Aug 24, 2017
@cheenamalhotra cheenamalhotra added Under Review Used for pull requests under review Work in Progress The pull request is a work in progress and removed PR Under Review labels Aug 24, 2017
@peterbae
Copy link
Contributor

peterbae commented Aug 31, 2017

Thanks for the contribution. as @JohnA2 suggested, I believe it would be the best approach to remove using ThreadLocal in this case, for a couple of reasons:

  1. As discussed in the comments, if the user has a web application running on an application server with a connection pool, it is difficult to signal the individual worker threads to call ThreadLocal.remove() since the thread that manages the connection pool is likely not a worker thread, and calling close() on the thread that manages the connection pool will affect the worker thread.

  2. We don't necessarily need ThreadLocal in this case since the ThreadLocal was being used for conveniently associating a thread with its own ActivityId object, and was not being used to increase performance and we can achieve thread safety in a different approach. Therefore, having a static hashmap of <id, ActivityId> achieves the same goal without introducing classloader leak by using ThreadLocal.

I opened a new PR for this issue: #465

@cheenamalhotra cheenamalhotra removed Work in Progress The pull request is a work in progress Under Review Used for pull requests under review labels Sep 27, 2017
@cheenamalhotra cheenamalhotra removed this from the Long Term Goals milestone Sep 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants