|
From: <jue...@we...> - 2004-10-19 16:33:26
|
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
|
|
From: Andy D. <an...@ma...> - 2004-10-19 17:38:01
|
People are beginning to use Spring to do dependency injection and apply=20 aspects (CGLIB) for objects created at runtime (via Hibernate, for example)= =2E =20 Moreover, we have toyed with the idea of applying aspects to objects we=20 dynamically create at runtime to enforce certain security constraints and t= o=20 facilitate distributed object models in a cluster - in which case the aspec= ts=20 are chosen dynamically and could change quite frequently. The dynamic natu= re=20 of all this could land us in the middle of this issue. What would the=20 ramifications be if these proxy classes were modified to use weak reference= s=20 instead of strong? Is this even a possibility? - Andy On Tuesday 19 October 2004 09:35 am, j=FCrgen h=F6ller [werk3AT] wrote: > 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*. |
|
From: Rob H. <ro...@ca...> - 2004-10-19 18:23:18
|
All,
One note of interest is that wherever possible a proxy class is reused.
In order for a proxy class for the same target class to be resued the
advisors must be equal. In the case Jurgen shows this is not the case -
he uses two instances of DebugInterceptor and the default implementation
of equals() is to use ==. If you override equals() like in
NopInterceptor and equals() returns true for all advisors then the proxy
class will be reused.
Rob
jürgen höller [werk3AT] wrote:
>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 = new ProxyFactory();
> proxyFactory.setTarget(new TestBean() {
> private byte[] field = new byte[1000000];
> });
> proxyFactory.addAdvice(new DebugInterceptor());
> proxyFactory.setProxyTargetClass(true);
> TestBean tb = (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
>
>
>-------------------------------------------------------
>This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
>Use IT products in your business? Tell us what you think of them. Give us
>Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out more
>http://productguide.itmanagersjournal.com/guidepromo.tmpl
>_______________________________________________
>Springframework-developer mailing list
>Spr...@li...
>https://lists.sourceforge.net/lists/listinfo/springframework-developer
>
>
>
>
|