Hi Mark,

On 23.09.2012 11:43, ma...@apache.org wrote:
Author: markt
Date: Sun Sep 23 09:43:43 2012
New Revision: 1388991

URL: http://svn.apache.org/viewvc?rev=1388991&view=rev
Log:
Use CharBuffer rather than StringBuilder to build the access log message to:
- save a char[] to String conversion and the associated garbage
- allow buffers to be recycled also saving garbage
Reduces allocations due to the AccessLog from 43% to 27% of the overall 
allocations

Modified:
     tomcat/trunk/java/org/apache/catalina/valves/AccessLogValve.java
     tomcat/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
     tomcat/trunk/webapps/docs/config/valve.xml

Modified: tomcat/trunk/java/org/apache/catalina/valves/AccessLogValve.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/AccessLogValve.java?rev=1388991&r1=1388990&r2=1388991&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/AccessLogValve.java (original)
+++ tomcat/trunk/java/org/apache/catalina/valves/AccessLogValve.java Sun Sep 23 
09:43:43 2012
@@ -570,13 +573,28 @@ public class AccessLogValve extends Valv
       */
      protected boolean requestAttributesEnabled = false;

-    // ------------------------------------------------------------- Properties
+    /**
+     * Buffer pool used for log message generation. Pool used to reduce garbage
+     * generation.
+     */
+    private Queue<CharBuffer> charBuffers = new ConcurrentLinkedQueue<>();

      /**
-     * @return Returns the enabled.
+     * Log message buffers are usually recycled and re-used. To prevent
+     * excessive memory usage, if a buffer grows beyond this size it will be
+     * discarded. The default is 256 characters. This should be set to larger
+     * than the typical access log message size.
       */
-    public boolean getEnabled() {
-        return enabled;
+    private int maxLogMessageBufferSize = 256;
+
+    // ------------------------------------------------------------- Properties
+
+    public int getMaxLogMessageBufferSize() {
+        return maxLogMessageBufferSize;
+    }
+
+    public void setMaxLogMessageBufferSize(int maxLogMessageBufferSize) {
+        this.maxLogMessageBufferSize = maxLogMessageBufferSize;
      }

      /**
@@ -596,6 +614,13 @@ public class AccessLogValve extends Valv
      }

      /**
+     * @return Returns the enabled.
+     */
+    public boolean getEnabled() {
+        return enabled;
+    }
+
+    /**
       * @param enabled
       *            The enabled to set.
       */
@@ -933,13 +958,23 @@ public class AccessLogValve extends Valv
          long start = request.getCoyoteRequest().getStartTime();
          Date date = getDate(start + time);

-        StringBuilder result = new StringBuilder(128);
+        CharBuffer result = charBuffers.poll();
+        if (result == null) {

I'm just curious here: do you already know whether using a direct buffer would be better or worse (e.g. ByteBuffer.allocateDirect(128).asCharBuffer())?

+            result = CharBuffer.allocate(128);
+        }

          for (int i = 0; i < logElements.length; i++) {
              logElements[i].addElement(result, date, request, response, time);
          }

-        log(result.toString());
+        result.flip();
+        log(result);
+
+        // TODO - Make this configurable

256 -> maxLogMessageBufferSize ?
"<" -> "<=" ?

+        if (result.length() < 256) {
+            result.clear();
+            charBuffers.add(result);
+        }
      }



@@ -1676,19 +1713,19 @@ public class AccessLogValve extends Valv
          }

          @Override
-        public void addElement(StringBuilder buf, Date date, Request request,
+        public void addElement(CharBuffer buf, Date date, Request request,
                  Response response, long time) {
              if (millis) {
-                buf.append(time);
+                buf.append(Long.toString(time));
              } else {
                  // second
-                buf.append(time / 1000);
+                buf.append(Long.toString(time / 1000));
                  buf.append('.');

I think the following code originally was meant to improve performance by slicing the 3 digit number "remains" into a sequence of digits. I expect the below to be a bit more efficient and less complicated now that Long.toString() is used:

                  int remains = (int) (time % 1000);
-                buf.append(remains / 100);
+                buf.append(Long.toString(remains / 100));
                  remains = remains % 100;
-                buf.append(remains / 10);
-                buf.append(remains % 10);
+                buf.append(Long.toString(remains / 10));
+                buf.append(Long.toString(remains % 10));

buf.append(Long.toString(time % 1000));

Regards,

Rainer

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

Reply via email to