Author: markt
Date: Wed Feb 17 21:16:40 2016
New Revision: 1730946
URL: http://svn.apache.org/viewvc?rev=1730946&view=rev
Log:
Refactor class loading so JAR scanning does not trigger the caching of the
byte[] for every scanned class until the class is loaded.
Modified:
tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java
tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java
tomcat/trunk/webapps/docs/changelog.xml
Modified: tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java?rev=1730946&r1=1730945&r2=1730946&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java (original)
+++ tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java Wed Feb 17
21:16:40 2016
@@ -14,13 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.loader;
import java.net.URL;
-import java.security.cert.Certificate;
-import java.util.jar.Manifest;
+
+import org.apache.catalina.WebResource;
/**
* Resource entry.
@@ -55,23 +53,6 @@ public class ResourceEntry {
public URL source = null;
- /**
- * URL of the codebase from where the object was loaded.
- */
- public URL codeBase = null;
-
-
- /**
- * Manifest (if the resource was loaded from a JAR).
- */
- public Manifest manifest = null;
-
-
- /**
- * Certificates (if the resource was loaded from a JAR).
- */
- public Certificate[] certificates = null;
-
-
+ public WebResource webResource = null;
}
Modified:
tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java?rev=1730946&r1=1730945&r2=1730946&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java Wed
Feb 17 21:16:40 2016
@@ -41,6 +41,7 @@ import java.security.PermissionCollectio
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -140,8 +141,6 @@ public abstract class WebappClassLoaderB
private static final String CLASS_FILE_SUFFIX = ".class";
private static final String SERVICES_PREFIX = "/META-INF/services/";
- private static final Manifest MANIFEST_UNKNOWN = new Manifest();
-
static {
ClassLoader.registerAsParallelCapable();
JVM_THREAD_GROUP_NAMES.add(JVM_THREAD_GROUP_SYSTEM);
@@ -153,19 +152,16 @@ public abstract class WebappClassLoaderB
protected final String name;
protected final String path;
- protected final boolean manifestRequired;
- PrivilegedFindResourceByName(String name, String path, boolean
manifestRequired) {
+ PrivilegedFindResourceByName(String name, String path) {
this.name = name;
this.path = path;
- this.manifestRequired = manifestRequired;
}
@Override
public ResourceEntry run() {
- return findResourceInternal(name, path, manifestRequired);
+ return findResourceInternal(name, path);
}
-
}
@@ -911,14 +907,15 @@ public abstract class WebappClassLoaderB
if (entry == null) {
if (securityManager != null) {
PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path, false);
+ new PrivilegedFindResourceByName(name, path);
entry = AccessController.doPrivileged(dp);
} else {
- entry = findResourceInternal(name, path, false);
+ entry = findResourceInternal(name, path);
}
}
if (entry != null) {
url = entry.source;
+ entry.webResource = null;
}
if ((url == null) && hasExternalRepositories) {
@@ -931,7 +928,7 @@ public abstract class WebappClassLoaderB
else
log.debug(" --> Resource not found, returning null");
}
- return (url);
+ return url;
}
@@ -2399,10 +2396,10 @@ public abstract class WebappClassLoaderB
if (securityManager != null) {
PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path, true);
+ new PrivilegedFindResourceByName(name, path);
entry = AccessController.doPrivileged(dp);
} else {
- entry = findResourceInternal(name, path, true);
+ entry = findResourceInternal(name, path);
}
if (entry == null) {
@@ -2418,10 +2415,43 @@ public abstract class WebappClassLoaderB
if (clazz != null)
return clazz;
- if (entry.binaryContent == null) {
+ WebResource webResource = entry.webResource;
+ if (webResource == null) {
+ webResource = resources.getResource(path);
+ } else {
+ entry.webResource = null;
+ }
+
+ if (webResource == null) {
return null;
}
+ Manifest manifest = webResource.getManifest();
+ URL codeBase = webResource.getCodeBase();
+ Certificate[] certificates = webResource.getCertificates();
+ byte[] binaryContent = webResource.getContent();
+
+ if (transformers.size() > 0) {
+ // If the resource is a class just being loaded, decorate it
+ // with any attached transformers
+ String className = name.endsWith(CLASS_FILE_SUFFIX) ?
+ name.substring(0, name.length() -
CLASS_FILE_SUFFIX.length()) : name;
+ String internalName = className.replace(".", "/");
+
+ for (ClassFileTransformer transformer : this.transformers) {
+ try {
+ byte[] transformed = transformer.transform(
+ this, internalName, null, null, binaryContent);
+ if (transformed != null) {
+ binaryContent = transformed;
+ }
+ } catch (IllegalClassFormatException e) {
+
log.error(sm.getString("webappClassLoader.transformError", name), e);
+ return null;
+ }
+ }
+ }
+
// Looking up the package
String packageName = null;
int pos = name.lastIndexOf('.');
@@ -2435,12 +2465,10 @@ public abstract class WebappClassLoaderB
// Define the package (if null)
if (pkg == null) {
try {
- if (entry.manifest == null) {
- definePackage(packageName, null, null, null, null,
- null, null, null);
+ if (manifest == null) {
+ definePackage(packageName, null, null, null, null,
null, null, null);
} else {
- definePackage(packageName, entry.manifest,
- entry.codeBase);
+ definePackage(packageName, manifest, codeBase);
}
} catch (IllegalArgumentException e) {
// Ignore: normal error due to dual definition of
package
@@ -2455,10 +2483,9 @@ public abstract class WebappClassLoaderB
if (pkg != null) {
boolean sealCheck = true;
if (pkg.isSealed()) {
- sealCheck = pkg.isSealed(entry.codeBase);
+ sealCheck = pkg.isSealed(codeBase);
} else {
- sealCheck = (entry.manifest == null)
- || !isPackageSealed(packageName, entry.manifest);
+ sealCheck = (manifest == null) ||
!isPackageSealed(packageName, manifest);
}
if (!sealCheck)
throw new SecurityException
@@ -2469,9 +2496,8 @@ public abstract class WebappClassLoaderB
}
try {
- clazz = defineClass(name, entry.binaryContent, 0,
- entry.binaryContent.length,
- new CodeSource(entry.codeBase, entry.certificates));
+ clazz = defineClass(name, binaryContent, 0,
+ binaryContent.length, new CodeSource(codeBase,
certificates));
} catch (UnsupportedClassVersionError ucve) {
throw new UnsupportedClassVersionError(
ucve.getLocalizedMessage() + " " +
@@ -2481,10 +2507,6 @@ public abstract class WebappClassLoaderB
// Now the class has been defined, clear the elements of the local
// resource cache that are no longer required.
entry.loadedClass = clazz;
- entry.binaryContent = null;
- entry.codeBase = null;
- entry.manifest = null;
- entry.certificates = null;
// Retain entry.source in case of a getResourceAsStream() call on
// the class file after the class has been defined.
}
@@ -2522,11 +2544,9 @@ public abstract class WebappClassLoaderB
*
* @param name the resource name
* @param path the resource path
- * @param manifestRequired will load an associated manifest
* @return the loaded resource, or null if the resource isn't found
*/
- protected ResourceEntry findResourceInternal(final String name, final
String path,
- boolean manifestRequired) {
+ protected ResourceEntry findResourceInternal(final String name, final
String path) {
checkStateForResourceLoading(name);
@@ -2538,25 +2558,9 @@ public abstract class WebappClassLoaderB
ResourceEntry entry = resourceEntries.get(path);
if (entry != null) {
- if (manifestRequired && entry.manifest == MANIFEST_UNKNOWN) {
- // This resource was added to the cache when a request was made
- // for the resource that did not need the manifest. Now the
- // manifest is required, the cache entry needs to be updated.
- resource = resources.getClassLoaderResource(path);
- entry.manifest = resource.getManifest();
- }
return entry;
}
- boolean isClassResource = path.endsWith(CLASS_FILE_SUFFIX);
- boolean isCacheable = isClassResource;
- if (!isCacheable) {
- isCacheable = path.startsWith(SERVICES_PREFIX);
- }
-
-
- boolean fileNeedConvert = false;
-
resource = resources.getClassLoaderResource(path);
if (!resource.exists()) {
@@ -2565,29 +2569,27 @@ public abstract class WebappClassLoaderB
entry = new ResourceEntry();
entry.source = resource.getURL();
- entry.codeBase = resource.getCodeBase();
entry.lastModified = resource.getLastModified();
+ entry.webResource = resource;
+ boolean fileNeedConvert = false;
if (needConvert && path.endsWith(".properties")) {
fileNeedConvert = true;
}
/* Only cache the binary content if there is some content
* available and one of the following is true:
- * a) It is a class file since the binary content is only cached
- * until the class has been loaded
- * or
- * b) The file needs conversion to address encoding issues (see
+ * a) The file needs conversion to address encoding issues (see
* below)
* or
- * c) The resource is a service provider configuration file located
+ * b) The resource is a service provider configuration file located
* under META-INF/services
*
* In all other cases do not cache the content to prevent
* excessive memory usage if large resources are present (see
* https://bz.apache.org/bugzilla/show_bug.cgi?id=53081).
*/
- if (isCacheable || fileNeedConvert) {
+ if (path.startsWith(SERVICES_PREFIX) || fileNeedConvert) {
byte[] binaryContent = resource.getContent();
if (binaryContent != null) {
if (fileNeedConvert) {
@@ -2603,38 +2605,6 @@ public abstract class WebappClassLoaderB
}
}
entry.binaryContent = binaryContent;
- // The certificates and manifest are made available as a side
- // effect of reading the binary content
- entry.certificates = resource.getCertificates();
- }
- }
-
- if (manifestRequired) {
- entry.manifest = resource.getManifest();
- } else {
- entry.manifest = MANIFEST_UNKNOWN;
- }
-
- if (isClassResource && entry.binaryContent != null &&
- this.transformers.size() > 0) {
- // If the resource is a class just being loaded, decorate it
- // with any attached transformers
- String className = name.endsWith(CLASS_FILE_SUFFIX) ?
- name.substring(0, name.length() -
CLASS_FILE_SUFFIX.length()) : name;
- String internalName = className.replace(".", "/");
-
- for (ClassFileTransformer transformer : this.transformers) {
- try {
- byte[] transformed = transformer.transform(
- this, internalName, null, null, entry.binaryContent
- );
- if (transformed != null) {
- entry.binaryContent = transformed;
- }
- } catch (IllegalClassFormatException e) {
- log.error(sm.getString("webappClassLoader.transformError",
name), e);
- return null;
- }
}
}
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1730946&r1=1730945&r2=1730946&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Wed Feb 17 21:16:40 2016
@@ -80,6 +80,10 @@
Make checking for var and map replacement in RewriteValve a bit
stricter and
correct detection of colon in var replacement. (fschumacher)
</add>
+ <fix>
+ Refactor the web application class loader to reduce the impact of JAR
+ scanning on the memory footprint of the web application. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]