Bug 120473 - Don't Create Separate Weaving Adaptors for Reflection Loaders
Summary: Don't Create Separate Weaving Adaptors for Reflection Loaders
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: PC Windows XP
: P3 enhancement (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-12 20:04 EST by Ron Bodkin CLA
Modified: 2012-04-03 15:43 EDT (History)
2 users (show)

See Also:


Attachments
use the parent loader's adaptor, or skip weaving if null (nothing is woven into reflection delegates from the bootstrap loader) (2.56 KB, patch)
2005-12-12 20:05 EST, Ron Bodkin CLA
no flags Details | Diff
Cleaner version that just skips trying to weave in reflection loaders altogether. (2.30 KB, patch)
2005-12-12 20:23 EST, Ron Bodkin CLA
aclement: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ron Bodkin CLA 2005-12-12 20:04:06 EST
The attached patch allows reusing weaving adaptors for sun.reflect.DelegatingClassLoader loaders, which noticably improves load-time weaving performance and is always safe.

I found that in a typical app server environment, there can be a lot of sun.reflect.DelegatingClassLoader instances lying around. They appear to be generated by the Sun reflection implementation and are used to load a single generated acessor or method. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4474172 has some information on what these loaders do. The key thing is that there will never be an aop.xml file in one of them, so it's safe to share for them... 

In my memory optimized load-time weaving set up not creating separate worlds reduced heap overhead by a lot, e.g., 200%, since most of the overhead in my optimized version comes from loading worlds. This allowed me to go from 14 world instances to just 3.

In the version in CVS head, it allowed me to reduce overhead on another project from 90 mb to 70 mb and to reduce start-up times by 5-10%.
Comment 1 Ron Bodkin CLA 2005-12-12 20:05:57 EST
Created attachment 31599 [details]
use the parent loader's adaptor, or skip weaving if null (nothing is woven into reflection delegates from the bootstrap loader)
Comment 2 Ron Bodkin CLA 2005-12-12 20:23:31 EST
Created attachment 31601 [details]
Cleaner version that just skips trying to weave in reflection loaders altogether.

The earlier patch works because we will never be weaving into the reflection loader loaded types. This version makes that clearer & is a little faster.
Comment 3 Ron Bodkin CLA 2005-12-12 22:58:31 EST
An alternative strategy that also works is to make WeavingAdaptor.shouldWeaveName a public and static, and then have Aj.preProcess start with:
        if (loader == null || className == null || !WeavingAdaptor.shouldWeaveName(className.replace('/','.'))) {

This imposes a bit more overhead (requiring a check on each class being loaded) but it removes the dependency on the implementation class sun.reflect.DelegatingClassLoader
Comment 4 Matthew Webster CLA 2005-12-13 06:06:33 EST
Which of the following are you suggesting?
1. Don't create a Weaver for each DelegatingClassLoader
2. Weaving reflection delegates is unlikely/impossible so optimize for DelegatingClassLoaders by sharing state
3. Don't permit weaving of reflection delegates

Your patch seems to implement 3 while the summary requests 1 and the description describes 2. 

I agree with the solution in your 2nd patch rather than your comment concerning a new public method. Reflection delegates, unlike user generated Java proxies, can be considered part of the JVM implementation and should not be exposed to the weaver. A safe and efficient test in Aj.preProcess() would be the best approach.
Comment 5 Matthew Webster CLA 2005-12-13 09:13:21 EST
Ron,

I am not sure that this patch will give you the benefit that you suggest. I have written a testcase the causes the creation of a DelegatingClassLoader, an accompanying WeavingAdaptor and generated sun/reflect/XXX classes. However with fixes to bug 113511, bug 116626 and bug 117189 (all of which use the "enabled" flag) we find no aop.xml, issue a message and return without creating a World or Weaver. All subsequent calls to "WeavingAdaptor.weaveClass()" will return immediately. Have you picked up changes from CVS head recently?
Comment 6 Ron Bodkin CLA 2005-12-13 10:33:51 EST
Yes I have tested this with the latest version. The performance gains are noticable.

There are cases where you make reflective calls from within a ClassLoader where an aop.xml is visible (e.g., through a parent), i.e., the DelegatingClassLoader has a parent. I will try to provide a simple example ... if you can provide me a patch that reproduces your tests that would be helpful.

To the earlier question, I realized that there's no point in creating a weaver to weave reflection delegates because WeaverAdaptor.shouldWeaveName never does so. The first patch implemented #1 to achieve #2: it used the parent loader's adaptor (this actually might break if we generated types like around closures in the delegating loader).
Comment 7 Matthew Webster CLA 2005-12-13 11:42:46 EST
Ron,

I am still puzzled. According to the description of a DelegationClassLoader "The key thing is that there will never be an aop.xml
file in one of them ...", which is confirmed by my test, any attached WeavingAdaptor will be disabled. Your patch will simply prevent the creation of the adaptor. Are you saying that there are circumstances when a DelegatingClassLoader _can_ see an aop.xml or are you saying the footprint of the adaptor itself is high?
Comment 8 Ron Bodkin CLA 2005-12-13 13:57:58 EST
Here's an example I'm seeing inside of Tomcat. Basically, the DelegatingClassLoader has a parent and it delegates to the parent to discover the various aop.xml files. While there is not (and never can be) an aop.xml file inside the DelegatingClassLoader, it will use normal classloader delegation to find one from its parent loader.

As a result, the system pays a fairly hefty price of parsing and constructing a big tree of BCEL objects for every such delegating class loader. Just on startup, I found a single Web app had 75% of the total enabled worlds coming from these reflection delegate class loaders.

loader.parent is an org.apache.catalina.loader.WebappClassLoader

definitions includes all of the aspects I've deployed to shared/lib

The first stack trace where I see a non-empty set of definitions is:
ClassLoaderWeavingAdaptor.initialize(ClassLoader, IWeavingContext) line: 107
Aj$ExplicitlyInitializedClassLoaderWeavingAdaptor.initialize(ClassLoader, IWeavingContext) line: 135
Aj$ExplicitlyInitializedClassLoaderWeavingAdaptor.getWeavingAdaptor(ClassLoader, IWeavingContext) line: 140
Aj$WeaverContainer.getWeaver(ClassLoader, IWeavingContext) line: 106
Aj.preProcess(String, byte[], ClassLoader) line: 62
ClassPreProcessorAgentAdapter.transform(ClassLoader, String, Class<?>, ProtectionDomain, byte[]) line: 52
TransformerManager.transform(ClassLoader, String, Class, ProtectionDomain, byte[]) line: 122
InstrumentationImpl.transform(ClassLoader, String, Class, ProtectionDomain, byte[]) line: 155
Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain) line: not available [native method]
ClassDefiner.defineClass(String, byte[], int, int, ClassLoader) line: 45
MethodAccessorGenerator$1.run() line: 381
AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]
MethodAccessorGenerator.generate(Class, String, Class[], Class, Class[], int, boolean, boolean, Class) line: 377
MethodAccessorGenerator.generateConstructor(Class, Class[], Class[], int) line: 76
NativeConstructorAccessorImpl.newInstance(Object[]) line: 30
DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 27
Constructor<T>.newInstance(Object...) line: 494
Class<T>.newInstance0() line: 350
Class<T>.newInstance() line: 303
ObjectCreateRule.begin(AttributeList) line: 153
Digester.startElement(String, AttributeList) line: 528
SAXParser(AbstractSAXParser).startElement(QName, XMLAttributes, Augmentations) line: not available
SAXParser(AbstractXMLDocumentParser).emptyElement(QName, XMLAttributes, Augmentations) line: not available
XMLDTDValidator.emptyElement(QName, XMLAttributes, Augmentations) line: not available
XMLDocumentScannerImpl(XMLDocumentFragmentScannerImpl).scanStartElement() line: not available
XMLDocumentScannerImpl$ContentDispatcher(XMLDocumentFragmentScannerImpl$FragmentContentDispatcher).dispatch(boolean) line: not available
XMLDocumentScannerImpl(XMLDocumentFragmentScannerImpl).scanDocument(boolean) line: not available
XML11Configuration.parse(boolean) line: not available
XML11Configuration.parse(XMLInputSource) line: not available
SAXParser(XMLParser).parse(XMLInputSource) line: not available
SAXParser(AbstractSAXParser).parse(InputSource) line: not available
SAXParserImpl(SAXParser).parse(InputSource, HandlerBase) line: 344
SAXParserImpl(SAXParser).parse(InputStream, HandlerBase) line: 120
Digester.parse(InputStream) line: 755
ActionServlet.initMapping() line: 1332
ActionServlet.init() line: 466
ActionServlet(GenericServlet).init(ServletConfig) line: 211
StandardWrapper.loadServlet() line: 1091
StandardWrapper.load() line: 925
StandardContext.loadOnStartup(Container[]) line: 3857
StandardContext.start() line: 4118
StandardHost(ContainerBase).addChildInternal(Container) line: 759
StandardHost(ContainerBase).addChild(Container) line: 739
StandardHost.addChild(Container) line: 524
HostConfig.deployDescriptor(String, File, String) line: 589
HostConfig.deployDescriptors(File, String[]) line: 536
HostConfig.deployApps() line: 471
HostConfig.start() line: 1102
HostConfig.lifecycleEvent(LifecycleEvent) line: 311
LifecycleSupport.fireLifecycleEvent(String, Object) line: 119
StandardHost(ContainerBase).start() line: 1020
StandardHost.start() line: 718
StandardEngine(ContainerBase).start() line: 1012
StandardEngine.start() line: 442
StandardService.start() line: 450
StandardServer.start() line: 683
Catalina.start() line: 537
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 585
Bootstrap.start() line: 271
Bootstrap.main(String[]) line: 409
Comment 9 Adrian Colyer CLA 2005-12-13 17:23:12 EST
patch applied ... thanks.
Comment 10 Matthew Webster CLA 2005-12-14 06:33:49 EST
Ron,

I was wrong. You are right. I did not see an aop.xml configuaration for the DelegatingClassLoader (and a World) in my testcase because of the way we configure LTW in the harness when we fork using Ant (which is necessary to use -javaagent). We do not load aop.xml from META-INF but from a location specified using "-Daj.def". However this mechanism only applies to the System class loader (check in ClassLoaderWeavingAdaptor) so the DelegatingClassLoader did not see it. I have revised the testcase to copy the aop.xml to "${aj.sandbox}/META-INF" and now I see the problem. When complete I will attach the testcase.
Comment 11 Adrian Colyer CLA 2005-12-14 07:05:21 EST
fix available