JDK-8156649 : Using JNDI for LDAP access causes a classloader leak
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.naming
  • Affected Version: 8,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2016-05-06
  • Updated: 2016-08-04
  • Resolved: 2016-08-04
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Whenever LDAP is used in a web application, it causes a classloader leak after redeployment with the following GC root:

this     - value: org.apache.catalina.loader.WebappClassLoader #2
 <- <classLoader>     - class: org.example.LDAPLeakDemo, value: org.apache.catalina.loader.WebappClassLoader #2
  <- [10]     - class: java.lang.Object[], value: org.example.LDAPLeakDemo class LDAPLeakDemo
   <- [2]     - class: java.lang.Object[], value: java.lang.Object[] #3884
    <- backtrace     - class: javax.naming.directory.SchemaViolationException, value: java.lang.Object[] #3875
     <- readOnlyEx     - class: com.sun.jndi.toolkit.dir.HierMemDirCtx, value: javax.naming.directory.SchemaViolationException #1
      <- EMPTY_SCHEMA (sticky class)     - class: com.sun.jndi.ldap.LdapCtx, value: com.sun.jndi.toolkit.dir.HierMemDirCtx #1


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.	Make sure you have any version of Tomcat and Maven.
2.	Go to Tomcat���s conf directory, open tomcat-users.xml and add 
<role rolename="manager-gui"/><user username="admin" password="1" roles="manager-gui"/> inside <tomcat-users></ tomcat-users>
to be able to use Tomcat Web Application Manager.
3.	Download the test project from https://github.com/JohnA2/ldap-leak-demo/
4.	Go to project���s directory and run "mvn package" to build a .war.
5.	Go to Tomcat���s webapps directory, delete everything except the manager directory and copy the .war here.
6.	Run Tomcat���s start script (bin\startup.bat or bin/startup.sh) and open http://localhost:8080/manager/, use username admin and password 1.
7.	Click on Reload to redeploy the web application.
8.	Start Java VisualVM (jvisualvm) from JDK���s bin directory.
9.	Double click on ���Tomcat��� on the left panel, open the Monitor tab on the right panel, and click on Heap Dump.
10.	Click on OQL Console, enter a query ���select x from org.apache.catalina.loader.WebappClassLoader x���, and click on Execute.
11.	You should see three ���org.apache.catalina.loader.WebappClassLoader��� entries. Find one with the field state set to DESTROYED, this is the classloader for the undeployed web application.
12.	Right click on its instance and select Show Nearest GC Root.
13.	You should see the GC root from the bug description.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No classloader leak.
ACTUAL -
Classloader leak.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package org.example;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class LDAPLeakDemo implements ServletContextListener {
	public void contextInitialized(ServletContextEvent sce) {
		useLDAP();
	}
	
	public void contextDestroyed(ServletContextEvent sce) {}
	
	private void useLDAP() {
		Hashtable<String, Object> env = new Hashtable<String, Object>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, "ldap://ldap.forumsys.com:389");
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, "cn=read-only-admin,dc=example,dc=com");
		env.put(Context.SECURITY_CREDENTIALS, "password");
		try {
			DirContext ctx = null;
			try {
				ctx = new InitialDirContext(env);
				System.out.println("Created the initial context");
			} finally {
				if (ctx != null) {
					ctx.close(); 
					System.out.println("Closed the context");
				}
			}
		} catch (NamingException e) {
			e.printStackTrace();
		}
	}
}

---------- END SOURCE ----------


Comments
Duplicate of JDK-8156824.
04-08-2016

Attached is the war file that is built on JDK8u92 with maven. Deployed this on Apache Tomcat 8.0.30. The attached screenshot is from jvisualvm, and shows details exactly as described in bug report. Haven't verified in JDK 9 because of lack of support in maven and Tomcat. Test result: JDK 8u92 - fail JDK 9- unverified
10-05-2016