Hi,
I have found, that once loaded jsp-servlets are never unloaded.
To test I just configured tomcat to process *.html files by JspServlet
and then traversed jdk documentation. The result was not very exciting -
after browsing ~ 150 pages tomcat cried "java.lang.OutOfMemoryError:
Java heap space"
and started not to work...
So maybe it would be not a bad idea to try to keep in memeory just
some fixed
number of jsp-servlets ?
I have written a sample implementation of such a policy, but it is not
very elegant
as internal structure containing jsp-servlets, it seems, was not
designed for such actions...
Regards,
Yarick.
------------------------------------------------------------------------
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
2006-01-03 10:14:04.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/EmbeddedServletOptions.java
2006-02-21 13:26:44.984221000 +0100
@@ -174,6 +174,17 @@
*/
private boolean xpoweredBy;
+ /**
+ * The maxim number of loaded jsps per web-application. If there are more
+ * jsps loaded, they will be unloaded.
+ */
+ private int maxLoadedJsps = 20;
+
+ /**
+ * How often it is tryed to unload jsps (in seconds)
+ */
+ private int jspUnloadTestInterval = 4;
+
public String getProperty(String name ) {
return settings.getProperty( name );
}
@@ -355,6 +366,14 @@
return null;
}
+ public int getMaxLoadedJsps() {
+ return maxLoadedJsps;
+ }
+
+ public int getJspUnloadTestInterval() {
+ return jspUnloadTestInterval;
+ }
+
/**
* Create an EmbeddedServletOptions object using data available from
* ServletConfig and ServletContext.
@@ -636,6 +655,40 @@
}
}
+ String maxLoadedJsps = config.getInitParameter("maxLoadedJsps");
+ if (maxLoadedJsps != null) {
+ try {
+ this.maxLoadedJsps = Integer.parseInt(maxLoadedJsps);
+ if (this.maxLoadedJsps <= 0) {
+ this.maxLoadedJsps = 20;
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage("jsp.warning.maxLoadedJsps",
""+this.maxLoadedJsps));
+ }
+ }
+ } catch(NumberFormatException ex) {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage("jsp.warning.maxLoadedJsps",
""+this.maxLoadedJsps));
+ }
+ }
+ }
+
+ String jspUnloadTestInterval =
config.getInitParameter("jspUnloadTestInterval");
+ if (jspUnloadTestInterval != null) {
+ try {
+ this.jspUnloadTestInterval =
Integer.parseInt(jspUnloadTestInterval);
+ if (this.jspUnloadTestInterval <= 0) {
+ this.jspUnloadTestInterval = 4;
+ if (log.isWarnEnabled()) {
+
log.warn(Localizer.getMessage("jsp.warning.jspUnloadTestInterval",""+this.jspUnloadTestInterval));
+ }
+ }
+ } catch(NumberFormatException ex) {
+ if (log.isWarnEnabled()) {
+
log.warn(Localizer.getMessage("jsp.warning.jspUnloadTestInterval",""+this.jspUnloadTestInterval));
+ }
+ }
+ }
+
// Setup the global Tag Libraries location cache for this
// web-application.
tldLocationsCache = new TldLocationsCache(context);
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/JspC.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/JspC.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/JspC.java
2006-01-03 10:14:04.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/JspC.java
2006-02-21 13:27:07.568387400 +0100
@@ -448,6 +448,14 @@
return cache;
}
+ public int getMaxLoadedJsps() {
+ return 0;
+ }
+
+ public int getJspUnloadTestInterval() {
+ return 0;
+ }
+
/**
* Background compilation check intervals in seconds
*/
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/Options.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/Options.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/Options.java
2006-01-03 10:14:04.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/Options.java
2006-02-21 13:27:24.210173000 +0100
@@ -184,5 +184,16 @@
* @return the Map(String uri, TreeNode tld) instance.
*/
public Map getCache();
+
+ /**
+ * The maxim number of loaded jsps per web-application. If there are more
+ * jsps loaded, they will be unloaded.
+ */
+ public int getMaxLoadedJsps();
+
+ /**
+ * How often it is tryed to unload jsps (in seconds)
+ */
+ public int getJspUnloadTestInterval();
}
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
2006-01-03 10:14:02.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/compiler/JspRuntimeContext.java
2006-02-21 13:16:29.004201800 +0100
@@ -25,10 +25,7 @@
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.cert.Certificate;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.*; // yarick: java.util.HashMap -> java.util.*
import javax.servlet.ServletContext;
import javax.servlet.jsp.JspFactory;
@@ -148,8 +145,8 @@
/**
* Maps JSP pages to their JspServletWrapper's
*/
- private Map jsps = Collections.synchronizedMap( new HashMap());
-
+ private Map jsps = Collections.synchronizedMap( new LinkedHashMap( 16, 0.75f, true ) ); // yarick: HashMap -> LinkedHashMap
+
/**
* The background thread.
@@ -192,6 +189,21 @@
}
/**
+ * Get an already existing JspServletWrapper and increases services count.
+ *
+ * @param jspUri JSP URI
+ * @return JspServletWrapper for JSP
+ */
+ public JspServletWrapper getWrapperAndIncService(String jspUri) {
+ JspServletWrapper jswr;
+ synchronized( jsps ) {
+ jswr = (JspServletWrapper) jsps.get(jspUri);
+ if( null != jswr ) jswr.incService();
+ }
+ return jswr;
+ }
+
+ /**
* Remove a JspServletWrapper.
*
* @param jspUri JSP URI of JspServletWrapper to remove
@@ -521,4 +533,28 @@
}
+// { inserted by Yarick
+
+ /** must be called from synchronized by [EMAIL PROTECTED]
org.apache.jasper.servlet.JspServlet} context */
+ public JspServletWrapper getJspForUnload()
+ {
+ int MAX_UNLOADABLE_JSPS = 10;
+ if( jsps.size() > MAX_UNLOADABLE_JSPS ) {
+ JspServletWrapper jsw = null;
+ synchronized( jsps ) {
+ Iterator it = jsps.entrySet().iterator();
+ for( int rest_jsps=jsps.size(); rest_jsps > MAX_UNLOADABLE_JSPS
&& it.hasNext(); --rest_jsps ) {
+ jsw = (JspServletWrapper) ( (Map.Entry) it.next()
).getValue();
+ if( jsw.getExecutingServicesCount() == 0 &&
!jsw.isTagFile() ) {
+ it.remove();
+ return jsw;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+// } inserted by Yarick
+
}
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
2006-01-03 10:14:02.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServlet.java
2006-02-21 13:37:08.060784200 +0100
@@ -17,8 +17,9 @@
package org.apache.jasper.servlet;
import java.io.IOException;
+import java.io.File;
import java.lang.reflect.Constructor;
-import java.util.Enumeration;
+import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
@@ -97,8 +98,10 @@
options = new EmbeddedServletOptions(config, context);
}
rctxt = new JspRuntimeContext(context, options);
-
- if (log.isDebugEnabled()) {
+
+ startUnloadJspsThread(); // inserted by yarick
+
+ if (log.isDebugEnabled()) {
log.debug(Localizer.getMessage("jsp.message.scratch.dir.is",
options.getScratchDir().toString()));
log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets"));
@@ -278,7 +281,7 @@
if (log.isDebugEnabled()) {
log.debug("JspServlet.destroy()");
}
-
+ stopUnloadJspsThread(); // inserted by yarick
rctxt.destroy();
}
@@ -290,29 +293,99 @@
Throwable exception, boolean precompile)
throws ServletException, IOException {
- JspServletWrapper wrapper =
- (JspServletWrapper) rctxt.getWrapper(jspUri);
- if (wrapper == null) {
- synchronized(this) {
- wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
- if (wrapper == null) {
- // Check if the requested JSP page exists, to avoid
- // creating unnecessary directories and files.
- if (null == context.getResource(jspUri)) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- jspUri);
- return;
+ JspServletWrapper wrapper = null;
+ try {
+ wrapper = (JspServletWrapper)
rctxt.getWrapperAndIncService(jspUri);
+ if( null == wrapper )
+ synchronized(this) {
+ wrapper = (JspServletWrapper)
rctxt.getWrapperAndIncService(jspUri);
+ if (wrapper == null) {
+ // Check if the requested JSP page exists, to avoid
+ // creating unnecessary directories and files.
+ if (null == context.getResource(jspUri)) {
+
response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ jspUri);
+ return;
+ }
+ boolean isErrorPage = exception != null;
+ wrapper = new JspServletWrapper(config, options,
jspUri,
+ isErrorPage, rctxt);
+ rctxt.addWrapper(jspUri,wrapper);
}
- boolean isErrorPage = exception != null;
- wrapper = new JspServletWrapper(config, options, jspUri,
- isErrorPage, rctxt);
- rctxt.addWrapper(jspUri,wrapper);
+ wrapper.incService();
+ }
+
+ /* dances around making it not possible to call servlet.init()
before
+ * previous copy of servlet is unloaded ( called method
servlet.destroy() )
+ */
+ String _destroyingUri = destroyingUri;
+ if( wrapper.getJspUri().equals( _destroyingUri ) )
+ synchronized( _destroyingUri ) {
+ if( _destroyingUri == destroyingUri )
+ _destroyingUri.wait();
}
+
+ wrapper.service(request, response, precompile);
+
+ } catch( InterruptedException ignore ) {}
+ finally { // inserted by yarick
+ synchronized(this) {
+ if( null != wrapper ) wrapper.decService();
}
- }
+ }
+ }
- wrapper.service(request, response, precompile);
+// { inserted by yarick
+ private Thread unloadThread;
+ private String destroyingUri;
+ protected void startUnloadJspsThread() {
+ String pathToWebApp = context.getRealPath("/");
+ if( pathToWebApp.endsWith( File.separator ) )
+ pathToWebApp = pathToWebApp.substring( 0, pathToWebApp.length()-1
);
+
+ unloadThread = new Thread( "jspUnloader ["+pathToWebApp.substring(
pathToWebApp.lastIndexOf( File.separatorChar ) )+( "/" )+"]" ) {
+ public void run() { runUnloadJspsThread(); }
+ };
+ unloadThread.setDaemon( true );
+ unloadThread.start();
+ }
+
+ protected void stopUnloadJspsThread() {
+ if( null != unloadThread ) {
+ unloadThread.interrupt();
+ try { unloadThread.join( 10 * 1000 ); } // wait maximum 10 seconds
+ catch( InterruptedException ignore ) {}
+ unloadThread = null;
+ }
}
+ protected void runUnloadJspsThread() {
+ try {
+ while( !Thread.currentThread().isInterrupted() ) {
+ JspServletWrapper jsw;
+ synchronized( this ) {
+ jsw = rctxt.getJspForUnload();
+ if( null != jsw ) destroyingUri = new String(
jsw.getJspUri() );
+ }
+
+ if( null == jsw )
+ Thread.sleep( options.getJspUnloadTestInterval() * 1000 );
+ else
+ /* dances around making it not possible to call
servlet.init() before
+ * previous copy of servlet is unloaded ( called method
servlet.destroy() )
+ */
+ synchronized( destroyingUri ) {
+ try {
+ jsw.destroy();
+ } finally {
+ String prev_destroyingUri = destroyingUri;
+ destroyingUri = null;
+ prev_destroyingUri.notifyAll();
+ }
+ }
+ }
+ } catch( InterruptedException exit ) {}
+ }
+// } inserted by yarick
}
diff -rdu
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
---
apache-tomcat-5.5.15-src-orig/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
2006-01-03 10:14:04.000000000 +0100
+++
apache-tomcat-5.5.15-src-patched/jasper/jasper2/src/share/org/apache/jasper/servlet/JspServletWrapper.java
2006-02-21 13:24:43.999843400 +0100
@@ -86,6 +86,7 @@
private JasperException compileException;
private long servletClassLastModifiedTime;
private long lastModificationTest = 0L;
+ private int executingServicesCount; // yarick
/*
* JspServletWrapper for JSP pages.
@@ -397,6 +398,10 @@
}
}
+ synchronized public void incService() { ++executingServicesCount; } // yarick: inserted accounting of 'service'
+ synchronized public void decService() { --executingServicesCount; } //
yarick: inserted accounting of 'service'
+ String getJspUri() { return jspUri; } // yarick:
inserted
+
public void destroy() {
if (theServlet != null) {
theServlet.destroy();
@@ -416,6 +421,9 @@
this.lastModificationTest = lastModificationTest;
}
+ /** @return Returns count of currently executing of method [EMAIL PROTECTED] #service} */
+ public int getExecutingServicesCount() { return executingServicesCount; }
// yarick
+
/**
* <p>Attempts to construct a JasperException that contains helpful
information
* about what went wrong. Uses the JSP compiler system to translate the
line
------------------------------------------------------------------------
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]