Yordan Boev
Yordan Boev

Reputation: 341

Jersey 2.x leaking memory (Finalizer) with simple POST call (Multithreaded)

The following simple Jersey call is leaking Objects into the Finalizer queue:

public boolean startExperiment(Experiment experiment) {
        final Client client = ClientBuilder.newClient();
        Response response = null;
        RunDescriptor runDescr = experiment.getRunDescriptor();
        try {
            final WebTarget webTarget = client.target(ExperimentController.getInstance().getRunnerUrl()).path("runs").path("startNewAsyncrun");
            response = webTarget.request().post(Entity.entity(runDescr, MediaType.APPLICATION_JSON));
            if (response != null && response.getStatusInfo().getStatusCode() != Status.CREATED.getStatusCode()) {
                System.out.println("Error, Run not created! Response Info: " + response.getStatusInfo());
                return false;
            } else {
                return true;
            }
        } finally {
            if(response != null) {
                response.close();
            }
            client.close();
        }

    }

Jersey versions with which I tested: 2.18, 2.20, 2.22.2, 2.23.1. The code is run from multiple threads (each thread running it once every minute, for example). Also closing Response and Client should not be necessary, added it jsut in case to test if it would help. Eclipse MAT shows Finalizer queue to be growing with Objects of type:

Any help or thoughts on the matter is really appreciated!

Upvotes: 0

Views: 1265

Answers (2)

Nasir Mushtaq
Nasir Mushtaq

Reputation: 1

Was facing the same issue, Paul Samsotha's answer helped. Creating Client loads jersey jars and JARFile in java overrides finalize method putting lot of overhead on finalizer thread.

Upvotes: 0

Rob
Rob

Reputation: 6497

While I am not familiar with Eclipse MAT and what it is showing you, it is proper behavior for some objects to go through the Finalizer queue.

Under normal circumstances, any object that overrides the finalize method will be queued for finalization (which consists of calling the finalize method). The general idea is that when the garbage collector identifies an object as unreachable, it looks to see if the finalize method is overridden and has not yet been called. If finalize is defined and has not yet been called, then the object is queued for finalization, otherwise the memory is collected.

The finalizer appears to be implemented as a queue that is serviced by a single thread. The thread processes each object in the queue by calling finalize. After the call to finalize completes, the object is removed from the queue.

Subsequently, when the garbage collector again determines that the object is unreachable, it will find that finalize is overridden, but has already been called. The member is collected.

Note that using this feature delays collection of garbage. And while it is a convenient way to clean up an object, the JVM does not guarantee when or even if the finalize method will be called.

One related risk is "finalizer starvation." You mention "growing" so perhaps this is an issue you are facing. If you create enough garbage that requires finalization and if that finalization takes too long, you can actually run out of memory because the single finalizer queue cannot keep up. There are a number of ways to deal with this situation:

  • Make execution of finalize complete (infinite loops are bad) and quickly.
  • Avoid synchronization in finalize.
  • Make the Finalizer thread have the highest execution priority. This can be accomplished by creating a garbage instance that changes the thread priority in the finalizer - create one of these during app initialization.

If you are seeing starvation, use a thread dump to see what the Finalizer thread is working on.

Upvotes: 1

Related Questions