Skip to content

Commit

Permalink
Memory leak when using Jersey with HK2 eclipse-ee4j#5796
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos committed Feb 7, 2025
1 parent 382f69e commit 45a3b8d
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Payara Foundation and/or its affiliates.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -55,6 +55,7 @@
import org.glassfish.jersey.internal.util.collection.LazyValue;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.message.internal.MessageBodyFactory;
import org.glassfish.jersey.model.internal.CommonConfig;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
Expand Down Expand Up @@ -416,17 +417,12 @@ private ClientRuntime initRuntime() {

final ClientBootstrapBag bootstrapBag = new ClientBootstrapBag();
bootstrapBag.setManagedObjectsFinalizer(new ManagedObjectsFinalizer(injectionManager));

final ClientMessageBodyFactory.MessageBodyWorkersConfigurator messageBodyWorkersConfigurator =
new ClientMessageBodyFactory.MessageBodyWorkersConfigurator();

List<BootstrapConfigurator> bootstrapConfigurators = Arrays.asList(
new RequestScope.RequestScopeConfigurator(),
List<BootstrapConfigurator> bootstrapConfigurators = Arrays.asList(new RequestScope.RequestScopeConfigurator(),
new ParamConverterConfigurator(),
new ParameterUpdaterConfigurator(),
new RuntimeConfigConfigurator(runtimeCfgState),
new ContextResolverFactory.ContextResolversConfigurator(),
messageBodyWorkersConfigurator,
new MessageBodyFactory.MessageBodyWorkersConfigurator(),
new ExceptionMapperFactory.ExceptionMappersConfigurator(),
new JaxrsProviders.ProvidersConfigurator(),
new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
Expand Down Expand Up @@ -467,8 +463,6 @@ private ClientRuntime initRuntime() {
final ClientRuntime crt = new ClientRuntime(configuration, connector, injectionManager, bootstrapBag);

client.registerShutdownHook(crt);
messageBodyWorkersConfigurator.setClientRuntime(crt);

return crt;
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -16,6 +16,7 @@

package org.glassfish.jersey.client;

import java.io.InputStream;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -173,6 +174,8 @@ Runnable createRunnableForAsyncProcessing(ClientRequest request, final ResponseC

@Override
public void response(final ClientResponse response) {
InputStream in = response.getEntityStream();
request.getClientConfig().getClient().putClientRuntimeLifeCycle(in, ClientRuntime.this);
requestScope.runInScope(() -> processResponse(request, response, callback));
}

Expand Down Expand Up @@ -298,6 +301,8 @@ public ClientResponse invoke(final ClientRequest request) {

try {
response = connector.apply(addUserAgent(Stages.process(request, requestProcessingRoot), connector.getName()));
InputStream in = response.getEntityStream();
request.getClientConfig().getClient().putClientRuntimeLifeCycle(in, this);
} catch (final AbortException aborted) {
response = aborted.getAbortResponse();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -16,11 +16,13 @@

package org.glassfish.jersey.client;

import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
Expand Down Expand Up @@ -69,6 +71,8 @@ public SSLContext getDefaultSslContext() {
private final LinkedBlockingDeque<WeakReference<JerseyClient.ShutdownHook>> shutdownHooks =
new LinkedBlockingDeque<WeakReference<JerseyClient.ShutdownHook>>();
private final ReferenceQueue<JerseyClient.ShutdownHook> shReferenceQueue = new ReferenceQueue<JerseyClient.ShutdownHook>();
// Keeps ClientRuntime alive till InputStream is GCed
private final Map<InputStream, ClientRuntime> clientRuntimeLifeCycle = new WeakHashMap<>();

/**
* Client instance shutdown hook.
Expand Down Expand Up @@ -387,4 +391,8 @@ public JerseyClient preInitialize() {
config.preInitialize();
return this;
}

void putClientRuntimeLifeCycle(InputStream in, ClientRuntime cr) {
clientRuntimeLifeCycle.put(in, cr);
}
}
13 changes: 12 additions & 1 deletion tests/integration/jersey-4507/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -46,4 +46,15 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+UseG1GC</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -19,6 +19,7 @@
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientLifecycleListener;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.JerseyClient;
import org.glassfish.jersey.examples.sse.jersey.App;
import org.glassfish.jersey.examples.sse.jersey.DomainResource;
import org.glassfish.jersey.examples.sse.jersey.ServerSentEventsResource;
Expand All @@ -33,8 +34,13 @@

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
Expand All @@ -44,6 +50,7 @@
import java.util.concurrent.atomic.AtomicInteger;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class SSETest extends JerseyTest {
private static final int MAX_CLIENTS = 10;
Expand Down Expand Up @@ -123,13 +130,29 @@ public void testInboundEventReaderMultiple() throws Exception {
}

System.gc();
triggerCleanupOfWeakHashMap();
closeLatch.await(15_000, TimeUnit.MILLISECONDS);
// One ClientConfig is on the Client
// + COUNT of them is created by .register(SseFeature.class)
Assertions.assertEquals(COUNT + 1, atomicInteger.get());
Assertions.assertEquals(0, closeLatch.getCount());
}

// WeakHashMap does not clean after GC. It cleans after some operations. This triggers it.
private void triggerCleanupOfWeakHashMap() throws Exception {
Field field = JerseyClient.class.getDeclaredField("clientRuntimeLifeCycle");
field.setAccessible(true);
Map<InputStream, Object> clientRuntimeLifeCycle = (Map<InputStream, Object>) field.get(client());
assertEquals(0, clientRuntimeLifeCycle.size());
clientRuntimeLifeCycle.clear();
System.gc();
Thread.sleep(100);
// Required to invoke WeakHashMap#expungeStaleEntries
ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]);
clientRuntimeLifeCycle.put(in, new Object());
clientRuntimeLifeCycle.size();
}



public static class ClientRuntimeCloseVerifier implements ClientLifecycleListener {
Expand Down
52 changes: 52 additions & 0 deletions tests/integration/jersey-5796/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<version>2.47-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>jersey-5796</artifactId>
<name>jersey-tests-integration-jersey-5796</name>

<dependencies>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-bundle</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+UseG1GC</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit 45a3b8d

Please # to comment.