|
From: <jue...@we...> - 2004-10-19 18:02:10
|
I've not committed this yet, as I still need to merge changes in that =
were committed this afternoon ;-)
Juergen
-----Original Message-----
From: j=FCrgen h=F6ller [werk3AT]=20
Sent: Tuesday, October 19, 2004 7:11 PM
To: 'spr...@li...'
Subject: RE: CGLIB memory usage within class loader
BTW, I've polished Cglib2AopProxy's source code quite a bit: for =
example, it logs at debug level now (rather than info), and uses =
formatting consistent with the rest of Spring's sources.
I've actually already done this polishing a second time: The earlier one =
got lost. It shows up in CVS, but then seems to have got overwritten by =
a subsequent commit. There was also a JDK 1.4 dependency fix in there =
(Boolean.valueOf with a boolean argument), which got lost too.
So please, when you merge before a commit, double-check that you're not =
losing updates that someone else applied before you! It wasn't a big =
issue this time, but what if we don't notice it next time...
Juergen
-----Original Message-----
From: j=FCrgen h=F6ller [werk3AT]=20
Sent: Tuesday, October 19, 2004 6:35 PM
To: spr...@li...
Subject: CGLIB memory usage within class loader
I've just been made aware of the following by a werk3 colleague: When =
repeatedly creating CGLIB proxies for the same target class but for =
different advices - within the same class loader -, you'll create new =
proxied classes all the time, which won't get removed for the lifetime =
of the classes.
The generated classes themselves are not the problem here. However, each =
of those proxy classes seems to hold strong references to its advices =
and target object, through holding the ProxyCallbackFilter instance that =
was passed into the Enhancer on proxy creation. Note that the proxy =
*class* holds that reference, not the proxy *instance*.
The effect is that you can easily run out of memory if your advices or =
target objects occupy a large amount of memory, as none of those objects =
will get garbage collected as long as the CGLIB-generated proxy class is =
still hanging around in the class loader. This does *not* happen at all =
with JDK dynamic proxies.
Of course, such excessive proxy creation is not the usual case in a =
production application. Web app restart is not a problem either, as the =
class loader will be shut down (releasing the CGLIB-generated proxy =
classes). The usual scenario where this memory leak becomes a problem is =
test suites, with application contexts getting created per test method.
The following code snippet reproduces the issue:
while (true) {
ProxyFactory proxyFactory =3D new ProxyFactory();
proxyFactory.setTarget(new TestBean() {
private byte[] field =3D new byte[1000000];
});
proxyFactory.addAdvice(new DebugInterceptor());
proxyFactory.setProxyTargetClass(true);
TestBean tb =3D (TestBean) proxyFactory.getProxy();
Thread.sleep(100);
}
Depending on the max memory available to the VM, you'll run into an =
OutOfMemoryError sooner or later, as the target TestBean instance with =
its byte array of size 1000000 never gets garbage collected. Note that =
this does *not* happen when removing the addAdvice call, as we're just =
generating a single CGLIB proxy class then.
Is this behavior unavoidable? What do we recommend for such testing =
scenarios then? In particular for integration tests with Hibernate, a 5 =
MB leak per context creation is not really acceptable. Of course it's =
preferable to share the application context as far as possible, but =
there are still valid use cases for repeated creation...
Juergen
|