Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent thread and object leak #149

Merged
merged 5 commits into from
Apr 26, 2024

Conversation

jdpgrailsdev
Copy link
Contributor

@bluedenim We discovered two more leaks when using the appender with a router:

  1. The number of registered shutdown hooks grows unbounded as new appender instances are created
  2. The queue of LoggingEventCache instances grows unbounded as new appender instances are created

This PR attempts to fix both by:

  • De-registering the shutdown hook thread when the stop() method is called by Log4j
  • Removing the cache from the queue when the stop() method on the LoggingEventCache is called as part of the appender stop flow implemented previously.

I do wonder if the shutdown hook is even necessary at this point, even in the standard single appender creation flow. Might be worth doing some testing to see if the stop() implementation makes the shutdown hook unnecessary, though it may still be a good safety net when an application crashes and Log4j cannot complete its shutdown flow.

@@ -285,6 +285,11 @@ public boolean stop() {
cacheMonitor.shutDown();
}

if (null != instances) {
// Remove the stopped cache from the queue to avoid a leak
instances.remove(this);
Copy link
Owner

@bluedenim bluedenim Apr 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instances is a static final already set, so it wouldn't be null.

It might help to also do this in the dtor (or as close to one Java has):

    @Override
    protected void finalize() {
        try {
            instances.remove(this);
        } catch (IOException ex) {
            VansLogger.logger.error(
                String.format("LoggingEventCache %s cannot remove itself from instances: %s", this, ex)
            );
        }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bluedenim Forgive the lag in my reply (I was on vacation). I can remove the if check if you think it is worth doing. As for putting the logic in the finalize() method, that is a bit of an anti-pattern in Java. You don't know when finalize() will be called -- it happens when the JVM decides it needs to perform garbage collection, which may not be any time near to when the object is no longer used by the code. This means that in this case, it would not prevent the leak in all scenarios -- it would totally be dependent on the heap size and how much is in use. Hooking into Log4j2's lifecycle is the better approach IMHO as it will get called when Log4j2 stops the appender.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely keep the current code. I was proposing to ALSO put it in the finalize(). But it's not that crucial for the reason you mentioned.

The if test is also not a big deal; it's just not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to leave it as it is, given that finalize() is deprecated and is to be removed in a future JDK release. Suggested alternatives are to use try-with-resources (we would need to make the cache implement AutoCloseable) or to use the Cleaner API.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I will merge and do a release in the next day or 2

@bluedenim bluedenim merged commit 4029fed into bluedenim:master Apr 26, 2024
1 check passed
@bluedenim
Copy link
Owner

This is now in release 5.2.2. Thanks for contributing!

@jdpgrailsdev jdpgrailsdev deleted the fix-thread-leak branch April 29, 2024 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants