Author: dain
Date: Mon Jun 6 08:18:59 2005
New Revision: 180322
URL: http://svn.apache.org/viewcvs?rev=180322&view=rev
Log:
Fixed several memory leaks around class loaders not being garbage collected
Modified:
geronimo/trunk/modules/axis/src/java/org/apache/geronimo/axis/server/AxisWebServiceContainer.java
geronimo/trunk/modules/client/src/java/org/apache/geronimo/client/AppClientContainer.java
geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TCCLInterceptor.java
geronimo/trunk/modules/core/src/java/org/apache/geronimo/pool/ThreadPool.java
geronimo/trunk/modules/deploy-tool/src/java/org/apache/geronimo/deployment/Bootstrap.java
geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/Configuration.java
Modified: geronimo/trunk/modules/axis/src/java/org/apache/geronimo/axis/server/AxisWebServiceContainer.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/axis/src/java/org/apache/geronimo/axis/server/AxisWebServiceContainer.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/axis/src/java/org/apache/geronimo/axis/server/AxisWebServiceContainer.java
(original)
+++ geronimo/trunk/modules/axis/src/java/org/apache/geronimo/axis/server/AxisWebServiceContainer.java
Mon Jun 6 08:18:59 2005
@@ -101,62 +101,64 @@
messageContext.setProperty(REQUEST, req);
messageContext.setProperty(RESPONSE, res);
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
try {
- String characterEncoding = (String) requestMessage.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
- if (characterEncoding != null) {
- messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, characterEncoding);
- } else {
- messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
- }
+ try {
+ String characterEncoding = (String) requestMessage.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
+ if (characterEncoding != null) {
+ messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, characterEncoding);
+ } else {
+ messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
+ }
- String soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
- if (soapAction != null) {
- messageContext.setUseSOAPAction(true);
- messageContext.setSOAPActionURI(soapAction);
- }
+ String soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
+ if (soapAction != null) {
+ messageContext.setUseSOAPAction(true);
+ messageContext.setSOAPActionURI(soapAction);
+ }
- SOAPEnvelope env = requestMessage.getSOAPEnvelope();
- if (env != null && env.getSOAPConstants() != null) {
- messageContext.setSOAPConstants(env.getSOAPConstants());
- }
- SOAPService service = messageContext.getService();
+ SOAPEnvelope env = requestMessage.getSOAPEnvelope();
+ if (env != null && env.getSOAPConstants() != null) {
+ messageContext.setSOAPConstants(env.getSOAPConstants());
+ }
+ SOAPService service = messageContext.getService();
+
+ Thread.currentThread().setContextClassLoader(classLoader);
+ service.invoke(messageContext);
- Thread.currentThread().setContextClassLoader(classLoader);
- service.invoke(messageContext);
+ responseMessage = messageContext.getResponseMessage();
+ } catch (AxisFault fault) {
+ responseMessage = handleFault(fault, res, messageContext);
- responseMessage = messageContext.getResponseMessage();
- } catch (AxisFault fault) {
- responseMessage = handleFault(fault, res, messageContext);
-
- } catch (Exception e) {
- responseMessage = handleException(messageContext, res, e);
- }
- //TODO investigate and fix operation == null!
- if (messageContext.getOperation() != null) {
- if (messageContext.getOperation().getMep() == OperationType.ONE_WAY) {
- // No content, so just indicate accepted
+ } catch (Exception e) {
+ responseMessage = handleException(messageContext, res, e);
+ }
+ //TODO investigate and fix operation == null!
+ if (messageContext.getOperation() != null) {
+ if (messageContext.getOperation().getMep() == OperationType.ONE_WAY) {
+ // No content, so just indicate accepted
+ res.setStatusCode(202);
+ return;
+ } else if (responseMessage == null) {
+ responseMessage = handleException(messageContext, null, new RuntimeException("No
response for non-one-way operation"));
+ }
+ } else if (responseMessage == null) {
res.setStatusCode(202);
return;
- } else if (responseMessage == null) {
- responseMessage = handleException(messageContext, null, new RuntimeException("No
response for non-one-way operation"));
}
- } else if (responseMessage == null) {
- res.setStatusCode(202);
- return;
- }
- try {
- SOAPConstants soapConstants = messageContext.getSOAPConstants();
- String contentType1 = responseMessage.getContentType(soapConstants);
- res.setContentType(contentType1);
- // Transfer MIME headers to HTTP headers for response message.
- MimeHeaders responseMimeHeaders = responseMessage.getMimeHeaders();
- for (Iterator i = responseMimeHeaders.getAllHeaders(); i.hasNext(); ) {
- MimeHeader responseMimeHeader = (MimeHeader) i.next();
- res.setHeader(responseMimeHeader.getName(),
- responseMimeHeader.getValue());
- }
- //TODO discuss this with dims.
+ try {
+ SOAPConstants soapConstants = messageContext.getSOAPConstants();
+ String contentType1 = responseMessage.getContentType(soapConstants);
+ res.setContentType(contentType1);
+ // Transfer MIME headers to HTTP headers for response message.
+ MimeHeaders responseMimeHeaders = responseMessage.getMimeHeaders();
+ for (Iterator i = responseMimeHeaders.getAllHeaders(); i.hasNext(); )
{
+ MimeHeader responseMimeHeader = (MimeHeader) i.next();
+ res.setHeader(responseMimeHeader.getName(),
+ responseMimeHeader.getValue());
+ }
+ //TODO discuss this with dims.
// // synchronize the character encoding of request and response
// String responseEncoding = (String) messageContext.getProperty(
// SOAPMessage.CHARACTER_SET_ENCODING);
@@ -168,12 +170,15 @@
// log.info(Messages.getMessage("exception00"), e);
// }
// }
- //determine content type from message response
- contentType = responseMessage.getContentType(messageContext.
- getSOAPConstants());
- responseMessage.writeTo(res.getOutputStream());
- } catch (Exception e) {
- log.info(Messages.getMessage("exception00"), e);
+ //determine content type from message response
+ contentType = responseMessage.getContentType(messageContext.
+ getSOAPConstants());
+ responseMessage.writeTo(res.getOutputStream());
+ } catch (Exception e) {
+ log.info(Messages.getMessage("exception00"), e);
+ }
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
Modified: geronimo/trunk/modules/client/src/java/org/apache/geronimo/client/AppClientContainer.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/client/src/java/org/apache/geronimo/client/AppClientContainer.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/client/src/java/org/apache/geronimo/client/AppClientContainer.java
(original)
+++ geronimo/trunk/modules/client/src/java/org/apache/geronimo/client/AppClientContainer.java
Mon Jun 6 08:18:59 2005
@@ -87,12 +87,12 @@
public void main(String[] args) throws Exception {
Thread thread = Thread.currentThread();
- ClassLoader contextClassLoader = thread.getContextClassLoader();
- thread.setContextClassLoader(classLoader);
+ ClassLoader oldClassLoader = thread.getContextClassLoader();
TransactionContext oldTransactionContext = transactionContextManager.getContext();
TransactionContext currentTransactionContext = null;
Subject oldCurrentCaller = ContextManager.getCurrentCaller();
try {
+ thread.setContextClassLoader(classLoader);
ContextManager.setCurrentCaller(defaultSubject);
jndiContext.startClient(appClientModuleName, kernel, classLoader);
currentTransactionContext = transactionContextManager.newUnspecifiedTransactionContext();
@@ -109,7 +109,7 @@
} finally {
jndiContext.stopClient(appClientModuleName);
- thread.setContextClassLoader(contextClassLoader);
+ thread.setContextClassLoader(oldClassLoader);
transactionContextManager.setContext(oldTransactionContext);
currentTransactionContext.commit();
ContextManager.setCurrentCaller(oldCurrentCaller);
Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TCCLInterceptor.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TCCLInterceptor.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TCCLInterceptor.java
(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TCCLInterceptor.java
Mon Jun 6 08:18:59 2005
@@ -34,8 +34,8 @@
public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
Thread currentThread = Thread.currentThread();
ClassLoader oldClassLoader = currentThread.getContextClassLoader();
- currentThread.setContextClassLoader(classLoader);
try {
+ currentThread.setContextClassLoader(classLoader);
next.getConnection(connectionInfo);
} finally {
currentThread.setContextClassLoader(oldClassLoader);
@@ -45,8 +45,8 @@
public void returnConnection(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction)
{
Thread currentThread = Thread.currentThread();
ClassLoader oldClassLoader = currentThread.getContextClassLoader();
- currentThread.setContextClassLoader(classLoader);
try {
+ currentThread.setContextClassLoader(classLoader);
next.returnConnection(connectionInfo, connectionReturnAction);
} finally {
currentThread.setContextClassLoader(oldClassLoader);
Modified: geronimo/trunk/modules/core/src/java/org/apache/geronimo/pool/ThreadPool.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/core/src/java/org/apache/geronimo/pool/ThreadPool.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/core/src/java/org/apache/geronimo/pool/ThreadPool.java (original)
+++ geronimo/trunk/modules/core/src/java/org/apache/geronimo/pool/ThreadPool.java Mon Jun
6 08:18:59 2005
@@ -28,41 +28,46 @@
* @version $Rev$ $Date$
*/
public class ThreadPool implements Executor, GBeanLifecycle {
-
private PooledExecutor executor;
+ private ClassLoader classLoader;
- private int nextWorkerID = 0;
-
- public ThreadPool(final int poolSize, final String poolName, final long keepAliveTime,
final ClassLoader classLoader) {
+ public ThreadPool(int poolSize, String poolName, long keepAliveTime, ClassLoader classLoader)
{
PooledExecutor p = new PooledExecutor(poolSize);
p.abortWhenBlocked();
p.setKeepAliveTime(keepAliveTime);
p.setMinimumPoolSize(poolSize);
- p.setThreadFactory(new ThreadFactory() {
- public Thread newThread(Runnable arg0) {
- Thread thread = new Thread(arg0, poolName + " " + getNextWorkerID());
- thread.setContextClassLoader(classLoader);
- return thread;
- }
- });
+ p.setThreadFactory(new ThreadPoolThreadFactory(poolName, classLoader));
executor = p;
+ this.classLoader = classLoader;
}
public void execute(Runnable command) throws InterruptedException {
- executor.execute(command);
- }
-
- private synchronized int getNextWorkerID() {
- return nextWorkerID++;
+ PooledExecutor p;
+ synchronized(this) {
+ p = executor;
+ }
+ if (p == null) {
+ throw new IllegalStateException("ThreadPool has been stopped");
+ }
+ Runnable task = new ContextClassLoaderRunnable(command, classLoader);
+ p.execute(task);
}
public void doStart() throws Exception {
}
public void doStop() throws Exception {
- executor.shutdownNow();
+ PooledExecutor p;
+ synchronized(this) {
+ p = executor;
+ executor = null;
+ classLoader = null;
+ }
+ if (p != null) {
+ p.shutdownNow();
+ }
}
public void doFail() {
@@ -72,6 +77,57 @@
}
}
+ private static final class ThreadPoolThreadFactory implements ThreadFactory {
+ private final String poolName;
+ private final ClassLoader classLoader;
+
+ private int nextWorkerID = 0;
+
+ public ThreadPoolThreadFactory(String poolName, ClassLoader classLoader) {
+ this.poolName = poolName;
+ this.classLoader = classLoader;
+ }
+
+ public Thread newThread(Runnable arg0) {
+ Thread thread = new Thread(arg0, poolName + " " + getNextWorkerID());
+ thread.setContextClassLoader(classLoader);
+ return thread;
+ }
+
+ private synchronized int getNextWorkerID() {
+ return nextWorkerID++;
+ }
+ }
+
+ private static final class ContextClassLoaderRunnable implements Runnable {
+ private Runnable task;
+ private ClassLoader classLoader;
+
+ public ContextClassLoaderRunnable(Runnable task, ClassLoader classLoader) {
+ this.task = task;
+ this.classLoader = classLoader;
+ }
+
+ public void run() {
+ Runnable myTask = task;
+ ClassLoader myClassLoader = classLoader;
+
+ // clear fields so they can be garbage collected
+ task = null;
+ classLoader = null;
+
+ if (myClassLoader != null) {
+ // we asumme the thread classloader is already set to our final class loader
+ // because the only to access the thread is wrapped with the Runnable or
via the initial thread pool
+ try {
+ myTask.run();
+ } finally {
+ Thread.currentThread().setContextClassLoader(myClassLoader);
+ }
+ }
+ }
+ }
+
public static final GBeanInfo GBEAN_INFO;
static {
@@ -93,6 +149,4 @@
public static GBeanInfo getGBeanInfo() {
return GBEAN_INFO;
}
-
-
}
Modified: geronimo/trunk/modules/deploy-tool/src/java/org/apache/geronimo/deployment/Bootstrap.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/deploy-tool/src/java/org/apache/geronimo/deployment/Bootstrap.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/deploy-tool/src/java/org/apache/geronimo/deployment/Bootstrap.java
(original)
+++ geronimo/trunk/modules/deploy-tool/src/java/org/apache/geronimo/deployment/Bootstrap.java
Mon Jun 6 08:18:59 2005
@@ -121,8 +121,9 @@
public void bootstrap() throws Exception {
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(Bootstrap.class.getClassLoader());
try {
+ Thread.currentThread().setContextClassLoader(Bootstrap.class.getClassLoader());
+
// parse the deployment-system and j2ee-deployer plans
ConfigurationType deployerSystemConfig = ConfigurationDocument.Factory.parse(new
File(deployerSystemPlan)).getConfiguration();
ConfigurationType j2eeDeployerConfig = ConfigurationDocument.Factory.parse(new
File(j2eeDeployerPlan)).getConfiguration();
Modified: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/Configuration.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/Configuration.java?rev=180322&r1=180321&r2=180322&view=diff
==============================================================================
--- geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/Configuration.java
(original)
+++ geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/Configuration.java
Mon Jun 6 08:18:59 2005
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
@@ -35,6 +36,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.lang.reflect.Field;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
@@ -129,12 +131,12 @@
/**
* The names of all GBeans contained in this configuration.
*/
- private final Set objectNames;
+ private Set objectNames;
/**
* The classloadeder used to load the child GBeans contained in this configuration.
*/
- private final ConfigurationClassLoader configurationClassLoader;
+ private ConfigurationClassLoader configurationClassLoader;
/**
* The GBeanData for the GBeans contained in this configuration. These must be persisted
as a ByteArray, becuase
@@ -144,6 +146,21 @@
private byte[] gbeanState;
/**
+ * Base path used to resolve relative class path entries.
+ */
+ private final URL baseURL;
+
+ /**
+ * Parent of this configuration
+ */
+ private final ConfigurationParent parent;
+
+ /**
+ * The repositories used dependencies.
+ */
+ private final Collection repositories;
+
+ /**
* Only used to allow declaration as a reference.
*/
public Configuration() {
@@ -160,6 +177,9 @@
configurationClassLoader = null;
dependencies = null;
classPath = null;
+ baseURL = null;
+ parent = null;
+ repositories = null;
}
/**
@@ -194,8 +214,11 @@
this.objectName = JMXUtil.getObjectName(objectName);
this.id = id;
this.moduleType = moduleType;
+ this.baseURL = baseURL;
this.parentId = parentId;
+ this.parent = parent;
this.gbeanState = gbeanState;
+ this.repositories = repositories;
if (classPath != null) {
this.classPath = classPath;
} else {
@@ -211,7 +234,21 @@
this.domain = domain;
this.server = server;
+ }
+
+ public String getObjectName() {
+ return objectNameString;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+ public String getServer() {
+ return server;
+ }
+
+ public void doStart() throws Exception {
// build configurationClassLoader
URL[] urls = resolveClassPath(classPath, baseURL, dependencies, repositories);
log.debug("ClassPath for " + id + " resolved to " + Arrays.asList(urls));
@@ -248,7 +285,7 @@
log.trace("Registering GBean " + name);
kernel.loadGBean(gbeanData, configurationClassLoader);
objectNames.add(name);
- // todo change this to a dependency on the gbeanData itself as soon as we
add that feature
+ // todo change this to a dependency on the gbeanData itself as soon as we
add that feature
kernel.getDependencyManager().addDependency(name, this.objectName);
}
this.objectNames = objectNames;
@@ -292,18 +329,6 @@
return urls;
}
- public String getObjectName() {
- return objectNameString;
- }
-
- public String getDomain() {
- return domain;
- }
-
- public String getServer() {
- return server;
- }
-
private static void setGBeanBaseUrl(GBeanData gbeanData, URL baseUrl) {
GBeanInfo gbeanInfo = gbeanData.getGBeanInfo();
Set attributes = gbeanInfo.getAttributes();
@@ -316,9 +341,6 @@
}
}
- public void doStart() throws Exception {
- }
-
public void doStop() throws Exception {
log.info("Stopping configuration " + id);
@@ -354,6 +376,15 @@
}
}
+ // destroy the class loader
+ LogFactory.release(configurationClassLoader);
+ configurationClassLoader = null;
+ clearSoftCache(ObjectInputStream.class, "subclassAudits");
+ clearSoftCache(ObjectOutputStream.class, "subclassAudits");
+ clearSoftCache(ObjectStreamClass.class, "localDescs");
+ clearSoftCache(ObjectStreamClass.class, "reflectors");
+
+ // update the configuation store
if (configurationStore != null) {
ConfigurationData configurationData = new ConfigurationData();
configurationData.setId(id);
@@ -365,6 +396,23 @@
configurationData.setDependencies(dependencies);
configurationData.setClassPath(classPath);
configurationStore.updateConfiguration(configurationData);
+ }
+ }
+
+ private static void clearSoftCache(Class clazz, String fieldName) {
+ Map cache = null;
+ try {
+ Field f = clazz.getDeclaredField(fieldName);
+ f.setAccessible(true);
+ cache = (Map) f.get(null);
+ } catch (Throwable e) {
+ log.error("Unable to clear SoftCache field " + fieldName + " in class " + clazz);
+ }
+
+ if (cache != null) {
+ synchronized (cache) {
+ cache.clear();
+ }
}
}
|