You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/03/07 20:22:32 UTC

[tomcat] branch 7.0.x updated: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63236

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/7.0.x by this push:
     new 25b1c2c  Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63236
25b1c2c is described below

commit 25b1c2c5b98d5cc2581a8a35249b2796558586be
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Mar 7 19:36:43 2019 +0000

    Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63236
    
    Use intern() as suggested by Phillip Webb to reduce memory wasted due to
    string duplication. Saves ~254k on a clean install.
    Thanks to YourKit for helping to track these down.
---
 .../apache/catalina/util/LifecycleMBeanBase.java   |   2 +-
 .../tomcat/util/digester/CallMethodRule.java       |   2 +-
 java/org/apache/tomcat/util/digester/Digester.java | 233 ++++++++++-----------
 .../apache/tomcat/util/modeler/ManagedBean.java    |  50 ++---
 .../MbeansDescriptorsIntrospectionSource.java      |   4 +-
 webapps/docs/changelog.xml                         |   7 +
 6 files changed, 150 insertions(+), 148 deletions(-)

diff --git a/java/org/apache/catalina/util/LifecycleMBeanBase.java b/java/org/apache/catalina/util/LifecycleMBeanBase.java
index 2939d92..4ff80d5 100644
--- a/java/org/apache/catalina/util/LifecycleMBeanBase.java
+++ b/java/org/apache/catalina/util/LifecycleMBeanBase.java
@@ -238,7 +238,7 @@ public abstract class LifecycleMBeanBase extends LifecycleBase
         
         this.mserver = server;
         this.oname = name;
-        this.domain = name.getDomain();
+        this.domain = name.getDomain().intern();
 
         return oname;
     }
diff --git a/java/org/apache/tomcat/util/digester/CallMethodRule.java b/java/org/apache/tomcat/util/digester/CallMethodRule.java
index 4350ecc..8da692a 100644
--- a/java/org/apache/tomcat/util/digester/CallMethodRule.java
+++ b/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -404,7 +404,7 @@ public class CallMethodRule extends Rule {
             throws Exception {
 
         if (paramCount == 0) {
-            this.bodyText = bodyText.trim();
+            this.bodyText = bodyText.trim().intern();
         }
 
     }
diff --git a/java/org/apache/tomcat/util/digester/Digester.java b/java/org/apache/tomcat/util/digester/Digester.java
index dc92d90..f427b5b 100644
--- a/java/org/apache/tomcat/util/digester/Digester.java
+++ b/java/org/apache/tomcat/util/digester/Digester.java
@@ -5,15 +5,15 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */ 
+ */
 
 package org.apache.tomcat.util.digester;
 
@@ -200,12 +200,12 @@ public class Digester extends DefaultHandler2 {
      * in the input is entered, the matching rules are pushed onto this
      * stack. After the end tag is reached, the matches are popped again.
      * The depth of is stack is therefore exactly the same as the current
-     * "nesting" level of the input xml. 
+     * "nesting" level of the input xml.
      *
      * @since 1.6
      */
     protected ArrayStack<List<Rule>> matches = new ArrayStack<List<Rule>>(10);
-    
+
     /**
      * The class loader to use for instantiating application objects.
      * If not specified, the context class loader, or the class loader
@@ -225,7 +225,7 @@ public class Digester extends DefaultHandler2 {
      * The EntityResolver used by the SAX parser. By default it use this class
      */
     protected EntityResolver entityResolver;
-    
+
     /**
      * The URLs of entityValidator that have been registered, keyed by the public
      * identifier that corresponds.
@@ -341,7 +341,7 @@ public class Digester extends DefaultHandler2 {
      */
     protected boolean rulesValidation = false;
 
-    
+
     /**
      * Fake attributes map (attributes are often used for object creation).
      */
@@ -360,12 +360,12 @@ public class Digester extends DefaultHandler2 {
      */
     protected Log saxLog =
         LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
-    
-        
+
+
     /** Stacks used for interrule communication, indexed by name String */
     private HashMap<String,ArrayStack<Object>> stacksByName =
         new HashMap<String,ArrayStack<Object>>();
-    
+
     // ------------------------------------------------------------- Properties
 
     /**
@@ -376,7 +376,7 @@ public class Digester extends DefaultHandler2 {
      * @param prefix Prefix to look up
      */
     public String findNamespaceURI(String prefix) {
-        
+
         ArrayStack<String> stack = namespaces.get(prefix);
         if (stack == null) {
             return (null);
@@ -480,9 +480,9 @@ public class Digester extends DefaultHandler2 {
 
     /**
      * Return the SAXParserFactory we will use, creating one if necessary.
-     * @throws ParserConfigurationException 
-     * @throws SAXNotSupportedException 
-     * @throws SAXNotRecognizedException 
+     * @throws ParserConfigurationException
+     * @throws SAXNotSupportedException
+     * @throws SAXNotRecognizedException
      */
     public SAXParserFactory getFactory()
     throws SAXNotRecognizedException, SAXNotSupportedException,
@@ -596,10 +596,10 @@ public class Digester extends DefaultHandler2 {
      * @since 1.6
      */
     public Log getSAXLogger() {
-        
+
         return saxLog;
     }
-    
+
 
     /**
      * Sets the logger used for logging SAX-related information.
@@ -607,9 +607,9 @@ public class Digester extends DefaultHandler2 {
      * @param saxLog Log, not null
      *
      * @since 1.6
-     */    
+     */
     public void setSAXLogger(Log saxLog) {
-    
+
         this.saxLog = saxLog;
     }
 
@@ -644,7 +644,7 @@ public class Digester extends DefaultHandler2 {
 
     }
 
-    
+
     /**
      * Set the public id of the current file being parse.
      * @param publicId the DTD/Schema public's id.
@@ -652,8 +652,8 @@ public class Digester extends DefaultHandler2 {
     public void setPublicId(String publicId){
         this.publicId = publicId;
     }
-    
-    
+
+
     /**
      * Return the public identifier of the DTD we are currently
      * parsing under, if any.
@@ -775,7 +775,7 @@ public class Digester extends DefaultHandler2 {
 
     }
 
-    
+
     /**
      * Set the <code>Rules</code> implementation object containing our
      * rules collection and associated matching policy.
@@ -908,24 +908,24 @@ public class Digester extends DefaultHandler2 {
     /**
      * Return the XMLReader to be used for parsing the input document.
      *
-     * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a 
+     * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a
      * parser that contains a schema with a DTD.
      * @exception SAXException if no XMLReader can be instantiated
      */
     public XMLReader getXMLReader() throws SAXException {
         if (reader == null){
             reader = getParser().getXMLReader();
-        }        
-                               
-        reader.setDTDHandler(this);           
-        reader.setContentHandler(this);        
-        
+        }
+
+        reader.setDTDHandler(this);
+        reader.setContentHandler(this);
+
         if (entityResolver == null){
             reader.setEntityResolver(this);
         } else {
-            reader.setEntityResolver(entityResolver);           
+            reader.setEntityResolver(entityResolver);
         }
-        
+
         reader.setProperty(
                 "http://xml.org/sax/properties/lexical-handler", this);
 
@@ -1031,7 +1031,7 @@ public class Digester extends DefaultHandler2 {
         // Parse system properties
         bodyText = updateBodyText(bodyText);
 
-        // the actual element name is either in localName or qName, depending 
+        // the actual element name is either in localName or qName, depending
         // on whether the parser is namespace aware
         String name = localName;
         if ((name == null) || (name.length() < 1)) {
@@ -1041,7 +1041,7 @@ public class Digester extends DefaultHandler2 {
         // Fire "body" events for all relevant rules
         List<Rule> rules = matches.pop();
         if ((rules != null) && (rules.size() > 0)) {
-            String bodyText = this.bodyText.toString();
+            String bodyText = this.bodyText.toString().intern();
             for (int i = 0; i < rules.size(); i++) {
                 try {
                     Rule rule = rules.get(i);
@@ -1234,7 +1234,7 @@ public class Digester extends DefaultHandler2 {
             saxLog.debug("startDocument()");
         }
 
-        // ensure that the digester is properly configured, as 
+        // ensure that the digester is properly configured, as
         // the digester could be used as a SAX ContentHandler
         // rather than via the parse() methods.
         configure();
@@ -1251,7 +1251,7 @@ public class Digester extends DefaultHandler2 {
      * @param qName The qualified name (with prefix), or the empty
      *   string if qualified names are not available.\
      * @param list The attributes attached to the element. If there are
-     *   no attributes, it shall be an empty Attributes object. 
+     *   no attributes, it shall be an empty Attributes object.
      * @exception SAXException if a parsing error is to be reported
      */
     @Override
@@ -1259,20 +1259,20 @@ public class Digester extends DefaultHandler2 {
                              String qName, Attributes list)
             throws SAXException {
         boolean debug = log.isDebugEnabled();
-        
+
         if (saxLog.isDebugEnabled()) {
             saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
                     qName + ")");
         }
-        
+
         // Parse system properties
         list = updateAttributes(list);
-        
+
         // Save the body text accumulated for our surrounding element
         bodyTexts.push(bodyText);
         bodyText = new StringBuilder();
 
-        // the actual element name is either in localName or qName, depending 
+        // the actual element name is either in localName or qName, depending
         // on whether the parser is namespace aware
         String name = localName;
         if ((name == null) || (name.length() < 1)) {
@@ -1397,8 +1397,8 @@ public class Digester extends DefaultHandler2 {
     public void setEntityResolver(EntityResolver entityResolver){
         this.entityResolver = entityResolver;
     }
-    
-    
+
+
     /**
      * Return the Entity Resolver used by the SAX parser.
      * @return Return the Entity Resolver used by the SAX parser.
@@ -1415,27 +1415,27 @@ public class Digester extends DefaultHandler2 {
             saxLog.debug("resolveEntity('" + publicId + "', '" + systemId +
                     "', '" + baseURI + "')");
         }
-        
+
         // Has this system identifier been registered?
         String entityURL = null;
         if (publicId != null) {
             entityURL = entityValidator.get(publicId);
         }
-         
-        if (entityURL == null) { 
+
+        if (entityURL == null) {
             if (systemId == null) {
                 // cannot resolve
                 if (log.isDebugEnabled()) {
                     log.debug(" Cannot resolve entity: '" + publicId + "'");
                 }
                 return (null);
-                
+
             } else {
                 // try to resolve using system ID
                 if (log.isDebugEnabled()) {
                     log.debug(" Trying to resolve using system ID '" +
                             systemId + "'");
-                } 
+                }
                 entityURL = systemId;
                 // resolve systemId against baseURI if it is not absolute
                 if (baseURI != null) {
@@ -1453,12 +1453,12 @@ public class Digester extends DefaultHandler2 {
                 }
             }
         }
-        
+
         // Return an input source to our alternative URL
         if (log.isDebugEnabled()) {
             log.debug(" Resolving to alternate DTD '" + entityURL + "'");
-        }  
-        
+        }
+
         try {
             return (new InputSource(entityURL));
         } catch (Exception e) {
@@ -1534,7 +1534,7 @@ public class Digester extends DefaultHandler2 {
             log.warn("Parse Warning Error at line " + exception.getLineNumber() +
                 " column " + exception.getColumnNumber() + ": " +
                 exception.getMessage(), exception);
-            
+
             errorHandler.warning(exception);
         }
 
@@ -1560,7 +1560,7 @@ public class Digester extends DefaultHandler2 {
         getXMLReader().parse(input);
         return (root);
 
-    }   
+    }
     /**
      * Parse the content of the specified input source using this Digester.
      * Returns the root element from the object stack (if any).
@@ -1571,7 +1571,7 @@ public class Digester extends DefaultHandler2 {
      * @exception SAXException if a parsing exception occurs
      */
     public Object parse(InputSource input) throws IOException, SAXException {
- 
+
         configure();
         getXMLReader().parse(input);
         return (root);
@@ -1641,18 +1641,18 @@ public class Digester extends DefaultHandler2 {
      * This must be called before the first call to <code>parse()</code>.
      * </p><p>
      * <code>Digester</code> contains an internal <code>EntityResolver</code>
-     * implementation. This maps <code>PUBLICID</code>'s to URLs 
+     * implementation. This maps <code>PUBLICID</code>'s to URLs
      * (from which the resource will be loaded). A common use case for this
-     * method is to register local URLs (possibly computed at runtime by a 
+     * method is to register local URLs (possibly computed at runtime by a
      * classloader) for DTDs. This allows the performance advantage of using
      * a local version without having to ensure every <code>SYSTEM</code>
      * URI on every processed xml document is local. This implementation provides
      * only basic functionality. If more sophisticated features are required,
      * using {@link #setEntityResolver} to set a custom resolver is recommended.
      * </p><p>
-     * <strong>Note:</strong> This method will have no effect when a custom 
-     * <code>EntityResolver</code> has been set. (Setting a custom 
-     * <code>EntityResolver</code> overrides the internal implementation.) 
+     * <strong>Note:</strong> This method will have no effect when a custom
+     * <code>EntityResolver</code> has been set. (Setting a custom
+     * <code>EntityResolver</code> overrides the internal implementation.)
      * </p>
      * @param publicId Public identifier of the DTD to be resolved
      * @param entityURL The URL to use for reading this DTD
@@ -1765,7 +1765,7 @@ public class Digester extends DefaultHandler2 {
         addRule(pattern,
                 new CallMethodRule(
                                     methodName,
-                                    paramCount, 
+                                    paramCount,
                                     paramTypes));
 
     }
@@ -1794,7 +1794,7 @@ public class Digester extends DefaultHandler2 {
         addRule(pattern,
                 new CallMethodRule(
                                     methodName,
-                                    paramCount, 
+                                    paramCount,
                                     paramTypes));
 
     }
@@ -1837,18 +1837,18 @@ public class Digester extends DefaultHandler2 {
 
     /**
      * Add a "call parameter" rule.
-     * This will either take a parameter from the stack 
-     * or from the current element body text. 
+     * This will either take a parameter from the stack
+     * or from the current element body text.
      *
      * @param paramIndex The zero-relative parameter number
      * @param fromStack Should the call parameter be taken from the top of the stack?
      * @see CallParamRule
-     */    
+     */
     public void addCallParam(String pattern, int paramIndex, boolean fromStack) {
-    
+
         addRule(pattern,
                 new CallParamRule(paramIndex, fromStack));
-      
+
     }
 
     /**
@@ -1859,16 +1859,16 @@ public class Digester extends DefaultHandler2 {
      * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
      * where 0 is the top of the stack, 1 the next element down and so on
      * @see CallMethodRule
-     */    
+     */
     public void addCallParam(String pattern, int paramIndex, int stackIndex) {
-    
+
         addRule(pattern,
                 new CallParamRule(paramIndex, stackIndex));
-      
+
     }
-    
+
     /**
-     * Add a "call parameter" rule that sets a parameter from the current 
+     * Add a "call parameter" rule that sets a parameter from the current
      * <code>Digester</code> matching path.
      * This is sometimes useful when using rules that support wildcards.
      *
@@ -1879,9 +1879,9 @@ public class Digester extends DefaultHandler2 {
     public void addCallParamPath(String pattern,int paramIndex) {
         addRule(pattern, new PathCallParamRule(paramIndex));
     }
-    
+
     /**
-     * Add a "call parameter" rule that sets a parameter from a 
+     * Add a "call parameter" rule that sets a parameter from a
      * caller-provided object. This can be used to pass constants such as
      * strings to methods; it can also be used to pass mutable objects,
      * providing ways for objects to do things like "register" themselves
@@ -1899,15 +1899,15 @@ public class Digester extends DefaultHandler2 {
      * @see CallMethodRule
      *
      * @since 1.6
-     */    
-    public void addObjectParam(String pattern, int paramIndex, 
+     */
+    public void addObjectParam(String pattern, int paramIndex,
                                Object paramObj) {
-    
+
         addRule(pattern,
                 new ObjectParamRule(paramIndex, paramObj));
-      
+
     }
-    
+
     /**
      * Add a "factory create" rule for the specified parameters.
      * Exceptions thrown during the object creation process will be propagated.
@@ -2000,7 +2000,7 @@ public class Digester extends DefaultHandler2 {
      * @see FactoryCreateRule
      */
     public void addFactoryCreate(
-                                    String pattern, 
+                                    String pattern,
                                     String className,
                                     boolean ignoreCreateExceptions) {
 
@@ -2021,7 +2021,7 @@ public class Digester extends DefaultHandler2 {
      * @see FactoryCreateRule
      */
     public void addFactoryCreate(
-                                    String pattern, 
+                                    String pattern,
                                     Class<?> clazz,
                                     boolean ignoreCreateExceptions) {
 
@@ -2044,7 +2044,7 @@ public class Digester extends DefaultHandler2 {
      * @see FactoryCreateRule
      */
     public void addFactoryCreate(
-                                String pattern, 
+                                String pattern,
                                 String className,
                                 String attributeName,
                                 boolean ignoreCreateExceptions) {
@@ -2068,7 +2068,7 @@ public class Digester extends DefaultHandler2 {
      * @see FactoryCreateRule
      */
     public void addFactoryCreate(
-                                    String pattern, 
+                                    String pattern,
                                     Class<?> clazz,
                                     String attributeName,
                                     boolean ignoreCreateExceptions) {
@@ -2255,7 +2255,7 @@ public class Digester extends DefaultHandler2 {
      * @see SetPropertiesRule
      */
     public void addSetProperties(
-                                String pattern, 
+                                String pattern,
                                 String attributeName,
                                 String propertyName) {
 
@@ -2274,7 +2274,7 @@ public class Digester extends DefaultHandler2 {
      * @see SetPropertiesRule
      */
     public void addSetProperties(
-                                String pattern, 
+                                String pattern,
                                 String [] attributeNames,
                                 String [] propertyNames) {
 
@@ -2342,7 +2342,7 @@ public class Digester extends DefaultHandler2 {
      * Clear the current contents of the object stack.
      * <p>
      * Calling this method <i>might</i> allow another document of the same type
-     * to be correctly parsed. However this method was not intended for this 
+     * to be correctly parsed. However this method was not intended for this
      * purpose. In general, a separate Digester object should be created for
      * each document to be parsed.
      */
@@ -2356,10 +2356,10 @@ public class Digester extends DefaultHandler2 {
         log = null;
         saxLog = null;
         configured = false;
-        
+
     }
 
-    
+
     public void reset() {
         root = null;
         setErrorHandler(null);
@@ -2436,7 +2436,7 @@ public class Digester extends DefaultHandler2 {
     /**
      * Pushes the given object onto the stack with the given name.
      * If no stack already exists with the given name then one will be created.
-     * 
+     *
      * @param stackName the name of the stack onto which the object should be pushed
      * @param value the Object to be pushed onto the named stack.
      *
@@ -2456,9 +2456,9 @@ public class Digester extends DefaultHandler2 {
      *
      * <p><strong>Note:</strong> a stack is considered empty
      * if no objects have been pushed onto it yet.</p>
-     * 
+     *
      * @param stackName the name of the stack from which the top value is to be popped
-     * @return the top <code>Object</code> on the stack or or null if the stack is either 
+     * @return the top <code>Object</code> on the stack or or null if the stack is either
      * empty or has not been created yet
      * @throws EmptyStackException if the named stack is empty
      *
@@ -2472,14 +2472,14 @@ public class Digester extends DefaultHandler2 {
                 log.debug("Stack '" + stackName + "' is empty");
             }
             throw new EmptyStackException();
-            
+
         } else {
-        
+
             result = namedStack.pop();
         }
         return result;
     }
-    
+
     /**
      * <p>Gets the top object from the stack with the given name.
      * This method does not remove the object from the stack.
@@ -2488,9 +2488,9 @@ public class Digester extends DefaultHandler2 {
      * if no objects have been pushed onto it yet.</p>
      *
      * @param stackName the name of the stack to be peeked
-     * @return the top <code>Object</code> on the stack or null if the stack is either 
+     * @return the top <code>Object</code> on the stack or null if the stack is either
      * empty or has not been created yet
-     * @throws EmptyStackException if the named stack is empty 
+     * @throws EmptyStackException if the named stack is empty
      *
      * @since 1.6
      */
@@ -2500,11 +2500,11 @@ public class Digester extends DefaultHandler2 {
         if (namedStack == null ) {
             if (log.isDebugEnabled()) {
                 log.debug("Stack '" + stackName + "' is empty");
-            }        
+            }
             throw new EmptyStackException();
-        
+
         } else {
-        
+
             result = namedStack.peek();
         }
         return result;
@@ -2514,9 +2514,9 @@ public class Digester extends DefaultHandler2 {
      * <p>Is the stack with the given name empty?</p>
      * <p><strong>Note:</strong> a stack is considered empty
      * if no objects have been pushed onto it yet.</p>
-     * @param stackName the name of the stack whose emptiness 
+     * @param stackName the name of the stack whose emptiness
      * should be evaluated
-     * @return true if the given stack if empty 
+     * @return true if the given stack if empty
      *
      * @since 1.6
      */
@@ -2528,19 +2528,19 @@ public class Digester extends DefaultHandler2 {
         }
         return result;
     }
-    
+
     /**
-     * When the Digester is being used as a SAXContentHandler, 
+     * When the Digester is being used as a SAXContentHandler,
      * this method allows you to access the root object that has been
      * created after parsing.
-     * 
+     *
      * @return the root object that has been created after parsing
      *  or null if the digester has not parsed any XML yet.
      */
     public Object getRoot() {
         return root;
     }
-    
+
 
     // ------------------------------------------------ Parameter Stack Methods
 
@@ -2558,7 +2558,7 @@ public class Digester extends DefaultHandler2 {
      * <p>
      * <strong>Note</strong> This method may be called more than once.
      * Once only initialization code should be placed in {@link #initialize}
-     * or the code should take responsibility by checking and setting the 
+     * or the code should take responsibility by checking and setting the
      * {@link #configured} flag.
      * </p>
      */
@@ -2580,19 +2580,19 @@ public class Digester extends DefaultHandler2 {
         configured = true;
 
     }
-    
+
     /**
      * <p>
      * Provides a hook for lazy initialization of this <code>Digester</code>
-     * instance.  
+     * instance.
      * The default implementation does nothing, but subclasses
      * can override as needed.
      * Digester (by default) only calls this method once.
      * </p>
      *
      * <p>
-     * <strong>Note</strong> This method will be called by {@link #configure} 
-     * only when the {@link #configured} flag is false. 
+     * <strong>Note</strong> This method will be called by {@link #configure}
+     * only when the {@link #configured} flag is false.
      * Subclasses that override <code>configure</code> or who set <code>configured</code>
      * may find that this method may be called more than once.
      * </p>
@@ -2604,7 +2604,7 @@ public class Digester extends DefaultHandler2 {
         // Perform lazy initialization as needed
         // Nothing required by default
 
-    }    
+    }
 
     // -------------------------------------------------------- Package Methods
 
@@ -2623,7 +2623,7 @@ public class Digester extends DefaultHandler2 {
      * <p>Return the top object on the parameters stack without removing it.  If there are
      * no objects on the stack, return <code>null</code>.</p>
      *
-     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
      * See {@link #params}.</p>
      */
     public Object peekParams() {
@@ -2643,7 +2643,7 @@ public class Digester extends DefaultHandler2 {
      * and [getCount()-1] is the bottom element.  If the specified index
      * is out of range, return <code>null</code>.</p>
      *
-     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
      * See {@link #params}.</p>
      *
      * @param n Index of the desired element, where 0 is the top of the stack,
@@ -2665,7 +2665,7 @@ public class Digester extends DefaultHandler2 {
      * <p>Pop the top object off of the parameters stack, and return it.  If there are
      * no objects on the stack, return <code>null</code>.</p>
      *
-     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
      * See {@link #params}.</p>
      */
     public Object popParams() {
@@ -2686,7 +2686,7 @@ public class Digester extends DefaultHandler2 {
     /**
      * <p>Push a new object onto the top of the parameters stack.</p>
      *
-     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
      * See {@link #params}.</p>
      *
      * @param object The new object
@@ -2767,7 +2767,7 @@ public class Digester extends DefaultHandler2 {
     public SAXException createSAXException(String message) {
         return createSAXException(message, null);
     }
-    
+
 
     // ------------------------------------------------------- Private Methods
 
@@ -2782,17 +2782,13 @@ public class Digester extends DefaultHandler2 {
         if (list.getLength() == 0) {
             return list;
         }
-        
+
         AttributesImpl newAttrs = new AttributesImpl(list);
         int nAttributes = newAttrs.getLength();
         for (int i = 0; i < nAttributes; ++i) {
             String value = newAttrs.getValue(i);
             try {
-                String newValue = 
-                    IntrospectionUtils.replaceProperties(value, null, source);
-                if (value != newValue) {
-                    newAttrs.setValue(i, newValue);
-                }
+                newAttrs.setValue(i, IntrospectionUtils.replaceProperties(value, null, source).intern());
             }
             catch (Exception e) {
                 log.warn("Attribute [" + newAttrs.getLocalName(i) + "] failed to update and remains [" + value + "].", e);
@@ -2800,7 +2796,6 @@ public class Digester extends DefaultHandler2 {
         }
 
         return newAttrs;
-
     }
 
 
diff --git a/java/org/apache/tomcat/util/modeler/ManagedBean.java b/java/org/apache/tomcat/util/modeler/ManagedBean.java
index 0f2e44c..93b68b4 100644
--- a/java/org/apache/tomcat/util/modeler/ManagedBean.java
+++ b/java/org/apache/tomcat/util/modeler/ManagedBean.java
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -49,7 +49,7 @@ import javax.management.ServiceNotFoundException;
 public class ManagedBean implements java.io.Serializable {
 
     private static final long serialVersionUID = 1L;
-    
+
     private static final String BASE_MBEAN = "org.apache.tomcat.util.modeler.BaseModelMBean";
     // ----------------------------------------------------- Instance Variables
     static final Object[] NO_ARGS_PARAM = new Object[0];
@@ -69,7 +69,7 @@ public class ManagedBean implements java.io.Serializable {
 
     private Map<String,OperationInfo> operations =
         new HashMap<String,OperationInfo>();
-    
+
     protected String className = BASE_MBEAN;
     //protected ConstructorInfo constructors[] = new ConstructorInfo[0];
     protected String description = null;
@@ -81,9 +81,9 @@ public class ManagedBean implements java.io.Serializable {
     protected NotificationInfo notifications[] = new NotificationInfo[0];
     protected String type = null;
 
-    /** Constructor. Will add default attributes. 
-     *  
-     */ 
+    /** Constructor. Will add default attributes.
+     *
+     */
     public ManagedBean() {
         AttributeInfo ai=new AttributeInfo();
         ai.setName("modelerType");
@@ -92,7 +92,7 @@ public class ManagedBean implements java.io.Serializable {
         ai.setWriteable(false);
         addAttribute(ai);
     }
-    
+
     // ------------------------------------------------------------- Properties
 
 
@@ -383,8 +383,8 @@ public class ManagedBean implements java.io.Serializable {
                 clazz = Class.forName(getClassName());
             } catch (Exception e) {
             }
-          
-            if( clazz==null ) {  
+
+            if( clazz==null ) {
                 try {
                     ClassLoader cl= Thread.currentThread().getContextClassLoader();
                     if ( cl != null)
@@ -393,8 +393,8 @@ public class ManagedBean implements java.io.Serializable {
                     ex=e;
                 }
             }
-    
-            if( clazz==null) { 
+
+            if( clazz==null) {
                 throw new MBeanException
                     (ex, "Cannot load ModelMBean class " + getClassName());
             }
@@ -409,9 +409,9 @@ public class ManagedBean implements java.io.Serializable {
                      getClassName());
             }
         }
-        
+
         mbean.setManagedBean(this);
-        
+
         // Set the managed resource (if any)
         try {
             if (instance != null)
@@ -471,11 +471,11 @@ public class ManagedBean implements java.io.Serializable {
 
 
             // Construct and return a new ModelMBeanInfo object
-            info = new MBeanInfo(getClassName(), 
+            info = new MBeanInfo(getClassName(),
                                  getDescription(),
-                                 attributes, 
-                                 new MBeanConstructorInfo[] {}, 
-                                 operations, 
+                                 attributes,
+                                 new MBeanConstructorInfo[] {},
+                                 operations,
                                  notifications);
 //        try {
 //            Descriptor descriptor = info.getMBeanDescriptor();
@@ -520,7 +520,7 @@ public class ManagedBean implements java.io.Serializable {
 
     }
 
-    Method getGetter(String aname, BaseModelMBean mbean, Object resource) 
+    Method getGetter(String aname, BaseModelMBean mbean, Object resource)
             throws AttributeNotFoundException, ReflectionException {
 
         Method m = null;
@@ -529,7 +529,7 @@ public class ManagedBean implements java.io.Serializable {
         // Look up the actual operation to be used
         if (attrInfo == null)
             throw new AttributeNotFoundException(" Cannot find attribute " + aname + " for " + resource);
-        
+
         String getMethod = attrInfo.getGetMethod();
         if (getMethod == null)
             throw new AttributeNotFoundException("Cannot find attribute " + aname + " get method name");
@@ -558,7 +558,7 @@ public class ManagedBean implements java.io.Serializable {
         return m;
     }
 
-    public Method getSetter(String aname, BaseModelMBean bean, Object resource) 
+    public Method getSetter(String aname, BaseModelMBean bean, Object resource)
             throws AttributeNotFoundException, ReflectionException {
 
         Method m = null;
@@ -602,11 +602,11 @@ public class ManagedBean implements java.io.Serializable {
         return m;
     }
 
-    public Method getInvoke(String aname, Object[] params, String[] signature, BaseModelMBean bean, Object resource) 
+    public Method getInvoke(String aname, Object[] params, String[] signature, BaseModelMBean bean, Object resource)
             throws MBeanException, ReflectionException {
 
         Method method = null;
-        
+
         if (params == null)
             params = new Object[0];
         if (signature == null)
@@ -671,7 +671,7 @@ public class ManagedBean implements java.io.Serializable {
         }
         key.append(')');
 
-        return key.toString();
+        return key.toString().intern();
     }
 
 
@@ -686,6 +686,6 @@ public class ManagedBean implements java.io.Serializable {
         }
         key.append(')');
 
-        return key.toString();
+        return key.toString().intern();
     }
 }
diff --git a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
index c90a7a5..fa5c4d8 100644
--- a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
+++ b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
@@ -356,8 +356,8 @@ public class MbeansDescriptorsIntrospectionSource extends ModelerSource
                     for(int i=0; i<parms.length; i++ ) {
                         ParameterInfo pi=new ParameterInfo();
                         pi.setType(parms[i].getName());
-                        pi.setName( "param" + i);
-                        pi.setDescription("Introspected parameter param" + i);
+                        pi.setName(("param" + i).intern());
+                        pi.setDescription(("Introspected parameter param" + i).intern());
                         op.addParameter(pi);
                     }
                     mbean.addOperation(op);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 438bc5e..f6d55d8 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -73,6 +73,13 @@
         searching for nested groups when the JNDIRealm is configured with
         <code>roleNested</code> set to <code>true</code>. (markt)
       </fix>
+      <fix>
+        <bug>63236</bug>: Use <code>String.intern()</code> as suggested by
+        Phillip Webb to reduce memory wasted due to String duplication. This
+        changes saves ~245k when starting a clean installation. With additional
+        thanks to YourKit Java profiler for helping to track down the wasted
+        memory and the root causes. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


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