Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-dev] Optimizing LTW...

Just to follow up on Matthew's email, we spent some quality time today
looking at how to optimize the performance of the load-time weaving
implementation in AspectJ 5. We looked at the memory usage and time used on
start up of the glassbox inspector open source project inside Tomcat 5.5.9,
running JRockIt 1.5.0_02 on my laptop. 

Deploying the original unoptimized version of this to the shared/lib folder
of a Tomcat instance with a few Web apps in it takes 20s to start and about
145 M. Without any weaving, it is about 5s and 95 M.

We have already found two big "quick wins":

1) If there is NO aop.xml file that a classloader can read, it can be a
major improvement to set enabled=false. This alone cuts the memory use to
about 115M and startup time to about 16s. See
https://bugs.eclipse.org/bugs/show_bug.cgi?id=112817

2) Loading classes that can be loaded from the bootstrap classloader, and
constructing a ReflectionBasedReferenceTypeDelegate instead of loading
bytecodes to make a Bcel Delegate. This further cuts memory use to 105M and
15s startup time. This avoids creating several duplicate copies of bytecodes
for java.lang.* etc. classes. To make this work, we had to work around a bug
in the ReflectionBasedReferenceTypeDelegate handling of arrays (Matthew
updated a unit test case for that class to reproduce the bug). Note that
this forces some class loading that might not otherwise happen of bootstrap
classes. although in practice I think it would be safe.

We thought about trying to delegate loading to a parent classloader to try
to load bytes, but one nasty gotcha here is non-delegating classloaders like
Web application classloaders. I was able to experiment with a couple of
ideas that seem to work, although there's either a problem in one of them or
another bug in ReflectionBasedReferenceTypeDelegate.

1) Loading definitions of already loaded classes from this classloader
instead of from bytes (since ClassLoader.findLoadedClass is protected, I
tested this using setAccessible with a reflective call).

This actually *never* helps: before a class is ever loaded by the
classloader, it is predefined, and its bytes are resolved...

2) Loading from the parent loader IF the resource URL for the class is non
null and is equal to the resource URL from the loader in question (this
handles nondelegating loaders)

My limited testing given some bad bytecode output suggests that this one
doesn't help much in this case on startup:

#2 seems to not help in the case of Tomcat startup because almost all of the
shared classes are loaded from bootstrap: only 1 copy of the Tomcat
container classes is loaded anyhow. More on the BCEL output in a follow up
post.

I _am_ wondering where the remaining 10M of overhead is going...

Here are some modifications to BcelWorld to do #2:

	private ClassLoader resolutionLoader;
	private ClassLoader parentLoader;
	public ClassLoader getResolutionLoader() {
		return resolutionLoader;
	}

	public void setResolutionLoader(ClassLoader resolutionLoader) {
		this.resolutionLoader = resolutionLoader;
		parentLoader = resolutionLoader.getParent();
		hasResolutionLoader = true;
	}

	public void clearResolutionLoader() {
		hasResolutionLoader = false;
	}

	protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) {
        String name = ty.getName();
        JavaClass jc = null;
        if (hasResolutionLoader) {
			try {
				if (allowBoot) {
					String asRes = name.replace('.',
'/').concat(".class");
					// will I resolve this by delegating
to my parent?
					java.net.URL parentURL =
parent.getResource(asRes);
					if (parentURL != null &&
parentURL.equals(resolutionLoader.getResource(asRes))) {
						Class resultBoot =
Class.forName(name, false, parent);
						if (resultBoot != null) {
							return new
ReflectionBasedReferenceTypeDelegate(resultBoot, this, ty);

						}
					}
// commented out version that clearly works, using just the boostrap loader
//					Class resultBoot =
Class.forName(name, false, null);
//					if (resultBoot != null) {
//	        			return new
ReflectionBasedReferenceTypeDelegate(resultBoot, this, ty);

//					}
				}
			if (result != null) {
				return new
ReflectionBasedReferenceTypeDelegate(result, this, ty);
			}
			} catch (Throwable e) {
				// proceed with normal
			}
//. as before

Ron Bodkin
Chief Technology Officer
New Aspects of Software
w: (415) 824-4690




Back to the top