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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to