You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fh...@apache.org on 2007/03/25 19:19:39 UTC

svn commit: r522303 - in /tomcat/tc6.0.x/trunk: java/org/apache/coyote/http11/Http11NioProtocol.java java/org/apache/tomcat/util/net/NioEndpoint.java webapps/docs/config/http.xml

Author: fhanik
Date: Sun Mar 25 10:19:39 2007
New Revision: 522303

URL: http://svn.apache.org/viewvc?view=rev&rev=522303
Log:
Implemented a one time parachute for java heap oom. Should give the system enough room to properly report the error and clear the caches. everything else will be up to the developer at that time

Modified:
    tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
    tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java
    tomcat/tc6.0.x/trunk/webapps/docs/config/http.xml

Modified: tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java?view=diff&rev=522303&r1=522302&r2=522303
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java Sun Mar 25 10:19:39 2007
@@ -34,13 +34,13 @@
 import org.apache.coyote.RequestGroupInfo;
 import org.apache.coyote.RequestInfo;
 import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.net.NioChannel;
 import org.apache.tomcat.util.net.NioEndpoint;
 import org.apache.tomcat.util.net.NioEndpoint.Handler;
-import org.apache.tomcat.util.res.StringManager;
-import org.apache.tomcat.util.net.NioChannel;
 import org.apache.tomcat.util.net.SSLImplementation;
 import org.apache.tomcat.util.net.SecureNioChannel;
 import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.res.StringManager;
 
 
 /**
@@ -534,6 +534,11 @@
         setAttribute("timeout", "" + timeouts);
     }
 
+    public void setOomParachute(int oomParachute) {
+        ep.setOomParachute(oomParachute);
+        setAttribute("oomParachute",oomParachute);
+    }
+
     // --------------------  SSL related properties --------------------
 
     public String getKeystoreFile() { return ep.getKeystoreFile();}
@@ -585,6 +590,10 @@
         Http11ConnectionHandler(Http11NioProtocol proto) {
             this.proto = proto;
         }
+        
+        public void releaseCaches() {
+            recycledProcessors.clear();
+        }
 
         public SocketState event(NioChannel socket, SocketStatus status) {
             Http11NioProcessor result = connections.get(socket);
@@ -742,6 +751,10 @@
 
     public String getDomain() {
         return domain;
+    }
+
+    public int getOomParachute() {
+        return ep.getOomParachute();
     }
 
     public ObjectName preRegister(MBeanServer server,

Modified: tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java?view=diff&rev=522303&r1=522302&r2=522303
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java Sun Mar 25 10:19:39 2007
@@ -17,6 +17,7 @@
 
 package org.apache.tomcat.util.net;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -54,7 +55,6 @@
 import org.apache.tomcat.util.IntrospectionUtils;
 import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
 import org.apache.tomcat.util.res.StringManager;
-import java.io.File;
 
 /**
  * NIO tailored thread pool, providing the following services:
@@ -160,7 +160,32 @@
     /**
      * use send file
      */
-    private boolean useSendfile = true;
+    protected boolean useSendfile = true;
+    
+    /**
+     * The size of the OOM parachute.
+     */
+    protected int oomParachute = 1024*1024;
+    /**
+     * The oom parachute, when an OOM error happens, 
+     * will release the data, giving the JVM instantly 
+     * a chunk of data to be able to recover with.
+     */
+    protected byte[] oomParachuteData = null;
+    
+    /**
+     * Make sure this string has already been allocated
+     */
+    protected static final String oomParachuteMsg = 
+        "SEVERE:Memory usage is low, parachute is non existent, your system may start failing.";
+    
+    /**
+     * Keep track of OOM warning messages.
+     */
+    long lastParachuteCheck = System.currentTimeMillis();
+    
+    
+    
     
     
     /**
@@ -587,13 +612,48 @@
         this.useSendfile = useSendfile;
     }
 
+    public void setOomParachute(int oomParachute) {
+        this.oomParachute = oomParachute;
+    }
+
+    public void setOomParachuteData(byte[] oomParachuteData) {
+        this.oomParachuteData = oomParachuteData;
+    }
+
     protected SSLContext sslContext = null;
     public SSLContext getSSLContext() { return sslContext;}
     public void setSSLContext(SSLContext c) { sslContext = c;}
     
-    // --------------------------------------------------------- Public Methods
-
+    // --------------------------------------------------------- OOM Parachute Methods
 
+    protected void checkParachute() {
+        boolean para = reclaimParachute(false);
+        if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000) {
+            try {
+                log.fatal(oomParachuteMsg);
+            }catch (Throwable t) {
+                System.err.println(oomParachuteMsg);
+            }
+            lastParachuteCheck = System.currentTimeMillis();
+        }
+    }
+    
+    protected boolean reclaimParachute(boolean force) {
+        if ( oomParachuteData != null ) return true;
+        if ( oomParachute > 0 && ( force || (Runtime.getRuntime().freeMemory() > (oomParachute*2))) )  
+            oomParachuteData = new byte[oomParachute];
+        return oomParachuteData != null;
+    }
+    
+    protected void releaseCaches() {
+        this.keyCache.clear();
+        this.nioChannels.clear();
+        this.processorCache.clear();
+        if ( handler != null ) handler.releaseCaches();
+        
+    }
+    
+    // --------------------------------------------------------- Public Methods
     /**
      * Number of keepalive sockets.
      */
@@ -664,6 +724,9 @@
             return;
 
         serverSock = ServerSocketChannel.open();
+        serverSock.socket().setPerformancePreferences(socketProperties.getPerformanceConnectionTime(),
+                                                      socketProperties.getPerformanceLatency(),
+                                                      socketProperties.getPerformanceBandwidth());
         InetSocketAddress addr = (address!=null?new InetSocketAddress(address,port):new InetSocketAddress(port));
         serverSock.socket().bind(addr,backlog); 
         serverSock.configureBlocking(true); //mimic APR behavior
@@ -698,6 +761,8 @@
             sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 
         }
+        
+        if (oomParachute>0) reclaimParachute(true);
 
         initialized = true;
 
@@ -803,7 +868,7 @@
 
 
     /**
-     * Deallocate APR memory pools, and close server socket.
+     * Deallocate NIO memory pools, and close server socket.
      */
     public void destroy() throws Exception {
         if (running) {
@@ -815,7 +880,7 @@
         serverSock = null;
         sslContext = null;
         initialized = false;
-        nioChannels.clear();
+        releaseCaches();
     }
 
 
@@ -850,6 +915,14 @@
         return useSendfile && (!isSSLEnabled());
     }
 
+    public int getOomParachute() {
+        return oomParachute;
+    }
+
+    public byte[] getOomParachuteData() {
+        return oomParachuteData;
+    }
+
     /**
      * Unlock the server socket accept using a bogus connection.
      */
@@ -1086,17 +1159,13 @@
      * Server socket acceptor thread.
      */
     protected class Acceptor implements Runnable {
-
-
         /**
          * The background thread that listens for incoming TCP/IP connections and
          * hands them off to an appropriate processor.
          */
         public void run() {
-
             // Loop until we receive a shutdown command
             while (running) {
-
                 // Loop if endpoint is paused
                 while (paused) {
                     try {
@@ -1105,7 +1174,6 @@
                         // Ignore
                     }
                 }
-
                 try {
                     // Accept the next incoming connection from the server socket
                     SocketChannel socket = serverSock.accept();
@@ -1124,16 +1192,24 @@
                             }
                         } 
                     }
+                } catch (OutOfMemoryError oom) {
+                    try {
+                        oomParachuteData = null;
+                        releaseCaches();
+                        log.error("", oom);
+                    }catch ( Throwable oomt ) {
+                        try {
+                            try {
+                                System.err.println(oomParachuteMsg);
+                                oomt.printStackTrace();
+                            }catch (Throwable letsHopeWeDontGetHere){}
+                        }catch (Throwable letsHopeWeDontGetHere){}
+                    }
                 } catch (Throwable t) {
                     log.error(sm.getString("endpoint.accept.fail"), t);
                 }
-
-                // The processor will recycle itself when it finishes
-
-            }
-
-        }
-
+            }//while
+        }//run
     }
 
 
@@ -1285,7 +1361,7 @@
                             ((PollerEvent)r).reset();
                             eventCache.offer((PollerEvent)r);
                         }
-                    } catch ( Exception x ) {
+                    } catch ( Throwable x ) {
                         log.error("",x);
                     }
                 }
@@ -1332,62 +1408,76 @@
         public void run() {
             // Loop until we receive a shutdown command
             while (running) {
-                // Loop if endpoint is paused
-                while (paused && (!close) ) {
-                    try {
-                        Thread.sleep(500);
-                    } catch (InterruptedException e) {
-                        // Ignore
-                    }
-                }
-                boolean hasEvents = false;
-
-                hasEvents = (hasEvents | events());
-                // Time to terminate?
-                if (close) {
-                    timeout(0, false);
-                    stopLatch.countDown();
-                    return;
-                }
-                int keyCount = 0;
                 try {
-                    if ( !close ) {
-                        keyCount = selector.select(selectorTimeout);
-                        wakeupCounter.set(0);
+                    // Loop if endpoint is paused
+                    while (paused && (!close) ) {
+                        try {
+                            Thread.sleep(500);
+                        } catch (InterruptedException e) {
+                            // Ignore
+                        }
                     }
+                    boolean hasEvents = false;
+
+                    hasEvents = (hasEvents | events());
+                    // Time to terminate?
                     if (close) {
                         timeout(0, false);
                         stopLatch.countDown();
-                        selector.close(); 
-                        return; 
+                        return;
                     }
-                } catch ( NullPointerException x ) {
-                    //sun bug 5076772 on windows JDK 1.5
-                    if ( wakeupCounter == null || selector == null ) throw x;
-                    continue;
-                } catch ( CancelledKeyException x ) {
-                    //sun bug 5076772 on windows JDK 1.5
-                    if ( wakeupCounter == null || selector == null ) throw x;
-                    continue;
-                } catch (Throwable x) {
-                    log.error("",x);
-                    continue;
-                }
-                //either we timed out or we woke up, process events first
-                if ( keyCount == 0 ) hasEvents = (hasEvents | events());
-
-                Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
-                // Walk through the collection of ready keys and dispatch
-                // any active event.
-                while (iterator != null && iterator.hasNext()) {
-                    SelectionKey sk = (SelectionKey) iterator.next();
-                    KeyAttachment attachment = (KeyAttachment)sk.attachment();
-                    iterator.remove();
-                    processKey(sk, attachment);
-                }//while
-                
-                //process timeouts
-                timeout(keyCount,hasEvents);
+                    int keyCount = 0;
+                    try {
+                        if ( !close ) {
+                            keyCount = selector.select(selectorTimeout);
+                            wakeupCounter.set(0);
+                        }
+                        if (close) {
+                            timeout(0, false);
+                            stopLatch.countDown();
+                            selector.close(); 
+                            return; 
+                        }
+                    } catch ( NullPointerException x ) {
+                        //sun bug 5076772 on windows JDK 1.5
+                        if ( wakeupCounter == null || selector == null ) throw x;
+                        continue;
+                    } catch ( CancelledKeyException x ) {
+                        //sun bug 5076772 on windows JDK 1.5
+                        if ( wakeupCounter == null || selector == null ) throw x;
+                        continue;
+                    } catch (Throwable x) {
+                        log.error("",x);
+                        continue;
+                    }
+                    //either we timed out or we woke up, process events first
+                    if ( keyCount == 0 ) hasEvents = (hasEvents | events());
+
+                    Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
+                    // Walk through the collection of ready keys and dispatch
+                    // any active event.
+                    while (iterator != null && iterator.hasNext()) {
+                        SelectionKey sk = (SelectionKey) iterator.next();
+                        KeyAttachment attachment = (KeyAttachment)sk.attachment();
+                        iterator.remove();
+                        processKey(sk, attachment);
+                    }//while
+
+                    //process timeouts
+                    timeout(keyCount,hasEvents);
+                    if ( oomParachute > 0 && oomParachuteData == null ) checkParachute();
+                } catch (OutOfMemoryError oom) {
+                    try {
+                        oomParachuteData = null;
+                        releaseCaches();
+                        log.error("", oom);
+                    }catch ( Throwable oomt ) {
+                        try {
+                            System.err.println(oomParachuteMsg);
+                            oomt.printStackTrace();
+                        }catch (Throwable letsHopeWeDontGetHere){}
+                    }
+                }
             }//while
             synchronized (this) {
                 this.notifyAll();
@@ -1395,7 +1485,7 @@
             stopLatch.countDown();
 
         }
-
+        
         protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
             boolean result = true;
             try {
@@ -1815,6 +1905,17 @@
                             ka.getPoller().add(socket,intops);
                         }
                     }
+                } catch (OutOfMemoryError oom) {
+                    try {
+                        oomParachuteData = null;
+                        releaseCaches();
+                        log.error("", oom);
+                    }catch ( Throwable oomt ) {
+                        try {
+                            System.err.println(oomParachuteMsg);
+                            oomt.printStackTrace();
+                        }catch (Throwable letsHopeWeDontGetHere){}
+                    }
                 } finally {
                     //dereference socket to let GC do its job
                     socket = null;
@@ -1874,6 +1975,7 @@
         }
         public SocketState process(NioChannel socket);
         public SocketState event(NioChannel socket, SocketStatus status);
+        public void releaseCaches();
     }
 
 
@@ -2000,6 +2102,18 @@
                 }
             }catch(CancelledKeyException cx) {
                 socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+            } catch (OutOfMemoryError oom) {
+                try {
+                    oomParachuteData = null;
+                    socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+                    releaseCaches();
+                    log.error("", oom);
+                }catch ( Throwable oomt ) {
+                    try {
+                        System.err.println(oomParachuteMsg);
+                        oomt.printStackTrace();
+                    }catch (Throwable letsHopeWeDontGetHere){}
+                }
             }catch ( Throwable t ) {
                 log.error("",t);
                 socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);

Modified: tomcat/tc6.0.x/trunk/webapps/docs/config/http.xml
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/config/http.xml?view=diff&rev=522303&r1=522302&r2=522303
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/config/http.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/config/http.xml Sun Mar 25 10:19:39 2007
@@ -561,6 +561,17 @@
         the property. If you do set it to false, you can control the size of the pool of selectors by using the 
         selectorPool.maxSelectors attribute</p>
       </attribute>
+      <attribute name="oomParachute" required="false">
+        <p>(int)The NIO connector implements an OutOfMemoryError strategy called parachute.
+           It holds a chunk of data as a byte array. In case of an OOM,
+           this chunk of data is released and the error is reported. This will give the VM enough room
+           to clean up. The <code>oomParachute</code> represent the size in bytes of the parachute(the byte array).
+           The default value is <code>1024*1024</code>(1MB).
+           Please note, this only works for OOM errors regarding the Java Heap space, and there is absolutely no 
+           guarantee that you will be able to recover at all.
+           If you have an OOM outside of the Java Heap, then this parachute trick will not help.
+        </p>
+      </attribute>
     </attributes>
   </subsection>
 



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org