eclipse-ee4j / eclipse-ee4j/jersey

ServiceHandleImpls leaking in filter handle when injecting RequestScoped and PerLookup services

Open
#4,381 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.