What makes you think your OOME has to do with the number of JspServlet instances? Run a profiler, you might be surprised.
Yoav On 3/3/06, Yaroslav Sokolov <[EMAIL PROTECTED]> wrote: > 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] > > -- Yoav Shapira Senior Architect Nimalex LLC 1 Mifflin Place, Suite 310 Cambridge, MA, USA [EMAIL PROTECTED] / www.yoavshapira.com --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]