You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xmlrpc-dev@ws.apache.org by Aren Sandersen <ar...@danger.com> on 2003/05/10 03:53:05 UTC
[PATCH] StringBuffers should not be blindly re-used in XmlRpc.java
This fixes a memory leak for those using Sun's 1.4.1 JRE. See a previous email by me for details. In a nutshell, StringBuffers should not be blindly re-used. If they are re-used, they should be destroyed and re-created after their underlying arrays have grown.
Aren
--------
--- src/java/org/apache/xmlrpc/XmlRpc.java.orig Fri May 9 14:24:55 2003
+++ src/java/org/apache/xmlrpc/XmlRpc.java Fri May 9 17:46:32 2003
@@ -102,6 +102,14 @@
*/
private static int maxThreads = 100;
+ /**
+ * The initial size of the cdata StringBuffer
+ *
+ * 32 seems like a good average between integer and string data
+ * lengths
+ */
+ private static final int cdataSize = 32;
+
String methodName;
/**
@@ -358,6 +366,32 @@
}
/**
+ * Clean the cdata StringBuffer object. If it has grown since we
+ * created it, re-create one at the original size
+ */
+ private void cleanCData()
+ {
+ // It is dangerous to re-use StringBuffers without checking the
+ // underlying capacity, due to a change in SUN JRE 1.4.1. This code
+ // will allow the StringBuffer to be re-used if the underlying
+ // capacity isn't too large, otherwise a new StringBuffer object
+ // is created.
+ //
+ // For details, please see:
+ //
+ // http://developer.java.sun.com/developer/bugParade/bugs/4724129.html
+
+ if (cdata.capacity() > cdataSize)
+ {
+ cdata = new StringBuffer(cdataSize);
+ }
+ else
+ {
+ cdata.setLength(0);
+ }
+ }
+
+ /**
* Parse the input stream. For each root level object, method
* <code>objectParsed</code> is called.
*/
@@ -369,11 +403,7 @@
values = new Stack ();
if (cdata == null)
{
- cdata = new StringBuffer(128);
- }
- else
- {
- cdata.setLength(0);
+ cdata = new StringBuffer(cdataSize);
}
readCdata = false;
currentValue = null;
@@ -420,11 +450,10 @@
}
finally
{
- // Clear any huge buffers.
- if (cdata.length() > 128 * 4)
+ // Don't keep StringBuffer around if it has a large underlying
+ // array. It's a waste of memory.
+ if (cdata.capacity() > cdataSize)
{
- // Exceeded original capacity by greater than 4x; release
- // buffer to prevent leakage.
cdata = null;
}
}
@@ -473,7 +502,7 @@
if (currentValue != null && readCdata)
{
currentValue.characterData(cdata.toString());
- cdata.setLength(0);
+ cleanCData();
readCdata = false;
}
@@ -515,7 +544,7 @@
else if ("methodName".equals(name))
{
methodName = cdata.toString();
- cdata.setLength(0);
+ cleanCData();
readCdata = false;
}
}
@@ -536,54 +565,53 @@
Value v = new Value();
values.push(v);
currentValue = v;
- // cdata object is reused
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("methodName".equals(name))
{
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("name".equals(name))
{
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("string".equals(name))
{
// currentValue.setType (STRING);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("i4".equals(name) || "int".equals(name))
{
currentValue.setType(INTEGER);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("boolean".equals(name))
{
currentValue.setType(BOOLEAN);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("double".equals(name))
{
currentValue.setType(DOUBLE);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("dateTime.iso8601".equals(name))
{
currentValue.setType(DATE);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("base64".equals(name))
{
currentValue.setType(BASE64);
- cdata.setLength(0);
+ cleanCData();
readCdata = true;
}
else if ("struct".equals(name))