eclipse-ee4j / eclipse-ee4j/jersey
ServiceHandleImpls leaking in filter handle when injecting RequestScoped and PerLookup services
- Dominant language
- Java
- Stars
- 730
- Forks
- 382
- PR merge metrics
- No merged PRs in 30d
Description
We recently had to inject a `RequestScoped` service into a filter (using a proxy). We noticed that CPU usage was spiking on our instances. After some investigation it turned out to be GC trashing due to a memory leak. We tracked the leak down to a single instance of `ServiceHandleImpl` (for the filter) holding onto ~ 1 million sub-handles. The sub-handles that were leaking all seemed to be `PerLookup`.
We were able to reproduce the leak with a simple `ResourceConfig` using 2.25.1:
```
public class TestResourceConfig extends ResourceConfig {
public TestResourceConfig() {
packages("com.leaktest");
register(new TestBinder());
register(TestFilter.class);
register(TestResource.class);
}
public class TestBinder extends AbstractBinder {
@Override
protected void configure() {
bindAsContract(PerlookupResourceService.class).in(PerLookup.class);
bind(DefaultRequestFilterService.class)
.to(RequestFilterService.class)
.in(RequestScoped.class)
.proxy(true)
.proxyForSameScope(false);
bindAsContract(PerlookupFilterService.class).in(PerLookup.class);
}
}
}
```
The injection graphs are as follows (-> injects):
`TestFilter` -> `RequestFilterService` -> `PerlookupFilterService`
`TestResource` -> `PerLookupResourceService`
Making several requests to `TestResource` results in the `ServiceHandleImpl` for `TestFilter` leaking handles of `PerlookupFilterService`.
If :
`bindAsContract(PerlookupFilterService.class).in(PerLookup.class);`
is changed to:
`bindAsContract(PerlookupFilterService.class).in(RequestScoped.class);`
the leak no longer occurs.
It looks as if `ServiceLocatorImpl.getService()` has the following condition on line 689:
```
if (PerLookup.class.equals(activeDescriptor.getScopeAnnotation())) {
rootImpl.addSubHandle(subHandle);
}
```
In this case `rootImpl` is the handle for the filter. When the `RequestScope` is destroyed the sub handle reference is never removed from `rootImpl`, and all references persist since `rootImpl` is Singleton scoped.
We are currently working around this issue by removing all `PerLookup` scoped services in the filter's injection hierarchy.
Contributor guide
Assessment
This issue has not been assessed yet.