[JERSEY-2677] Leak for RequestScoped injectables if async server model is used Created: 06/Oct/14 Updated: 10/Sep/15 Resolved: 19/Feb/15 Status: Project: Component/s: Affects Version/s: Fix Version/s: Closed jersey core, performance 2.12, 2.13 Type: Reporter: Resolution: Labels: Remaining Estimate: Time Spent: Original Estimate: Bug bodewig Fixed hk2, memory-leak Not Specified 2.17 Priority: Assignee: Votes: Critical Adam Lindenthal 0 Not Specified Not Specified Description The dispose method of a HK2 factory bound to RequestScoped is never called when the async server model is used. I intend to attach a simple unit test using grizzly but we see the same result using Jetty as runtime. It seems that the RequestScoped.Instance reference count is not decremented often enough to trigger disposal in the async case as we managed to work around the problem with a custom RequestListener that captures the current Instance for async requests and invokes release on it on the FINISHED event (twice as it is responsible for one reference itself). Comments Comment by bodewig [ 06/Oct/14 ] I can't attach anything here, is this correct? OK, here is my unit test inline package org.example.jersey_bug; import import import import import import import javax.inject.Inject; javax.ws.rs.GET; javax.ws.rs.Path; javax.ws.rs.container.AsyncResponse; javax.ws.rs.container.Suspended; javax.ws.rs.core.Application; javax.ws.rs.core.Response; import import import import import org.glassfish.hk2.api.Factory; org.glassfish.hk2.utilities.binding.AbstractBinder; org.glassfish.jersey.process.internal.RequestScoped; org.glassfish.jersey.server.ResourceConfig; org.glassfish.jersey.test.JerseyTest; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class RequestScopedAndAsyncTest extends JerseyTest { public static class Injectable { // just a placeholder } public static class InjectableFactory implements Factory<Injectable> { private static final Object LOCK = new Object(); private static int provided; private static int balance; @Override public Injectable provide() { synchronized(LOCK) { provided++; balance++; } return new Injectable(); } @Override public void dispose(Injectable i) { synchronized(LOCK) { balance--; } } public static void reset() { synchronized(LOCK) { provided = balance = 0; } } public static void assertProvidedOne() { synchronized(LOCK) { Assert.assertEquals(1, provided); } } public static void assertIsBalanced() { synchronized(LOCK) { Assert.assertEquals(0, balance); } } } @Path("test") public static class TestResource { @Inject private Injectable injectable; @GET @Path("sync") public Response sync() { return Response.noContent().build(); } @GET @Path("async") public void async(@Suspended AsyncResponse ar) { ar.resume(Response.noContent().build()); } } @Before public void resetCounters() { InjectableFactory.reset(); } @Override protected Application configure() { return new ResourceConfig(TestResource.class) .register(new AbstractBinder() { @Override protected void configure() { bindFactory(InjectableFactory.class) .to(Injectable.class) .in(RequestScoped.class); } }); } @Test public void shouldProvideAndDisposeSync() { Assert.assertEquals(204, target("/test/sync").request().get().getStatus()); InjectableFactory.assertProvidedOne(); InjectableFactory.assertIsBalanced(); } @Test public void shouldProvideAndDisposeAsync() { Assert.assertEquals(204, target("/test/async").request().get().getStatus()); InjectableFactory.assertProvidedOne(); InjectableFactory.assertIsBalanced(); } } Comment by Adam Lindenthal [ 09/Oct/14 ] Hi bodewig, correct - you cannot unfortunatelly attach files to the issue. But we can - next time just drop a link to github project or whatever (e.g. you can send it via email to one of us after the communication starts here) and we will attach it for you. Regarding the problem you are experiencing - this needs to be tested and investigated - thus I am moving it to backlog. Thanks, Adam PS: should you still have something more to attach here, feel free to drop a link or send a zip to me. Comment by Adam Lindenthal [ 19/Feb/15 ] Fixed in 2.17. Generated at Tue Feb 09 23:02:19 UTC 2016 using JIRA 6.2.3#6260sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.