I’m the author of a Java library that provides Java access to a C++ library our company sells. The instances of one of the classes “own” C++ objects in the following sense: The class has a few private long
fields that are set by certain native
methods and have the role of (unique) pointers into the C++ heap. Because the C++ heap is not garbage collected, the memory must be freed manually when the owning Java object doesn’t need them anymore, in particular when the owning Java object itself isn’t needed anymore, so the class implements AutoClosable
and close()
frees all the C++ memory owned by the instance. Ideally, users would use try
-with-resources on the instances of that class.
One customer complained that SonarQube warns them that close()
isn’t called to free resources and suggests to use try
-with-resources on the object, but the object is held by a static
variable or something equivalent and lives until the application closes (as far as I understood). I want to help them, but removing AutoClosable
(as they suggested) simply isn’t correct. The issue isn’t so much about the memory: When the application ends and the C++ library is unloaded, the memory resources are freed (by the OS) anyway.
So, how does one close AutoClosable
objects held by static variables, ideally in a way that SonarQube detects? Java does not seem to have the opposite of a class initializer that could be used.
I don’t have access to SonarQube to play around and see what could work, i.e. when SonarQube recognizes that close()
will be called. I’m about to tell them that they should appropriately reconfigure SonarQube or suppress the warning. Asking this question, I want to make sure it’s essentially the best course of action. “Yes, it is” would be an appropriate answer if it is true. Of course, not using global state, i.e. not having a static
AutoClosable
is a no-brainer, but I guess they already know that.
It seems like no-one had the problem of static
AutoClosable
objects, since there are no questions about it on Stack Overflow.
I figured that AutoClosable
is similar to IDisposable
in .NET (C#), and I found this question very much asking this, however the answers are specific to the use-case and .NET, and also don’t provide a general solution.
One customer complained that SonarQube warns them that close() isn’t called to free resources and suggests to use try-with-resources on the object, but the object is held by a static variable or something equivalent and lives until the application closes (as far as I understood).
"Doctor, it hurts when I smash a hammer into my face!"
Stop doing that then.
SonarQube is a tool. It can be configured to just say silly things, similar to that hammer. The fix isn't to try to grow a hammerproof face. The fix is to simply stop doing the thing whose only purpose is to hurt.
Unfortunately, your customer might not be willing to hear that.
But if they are, they can tell sonarqube to stop doing that. They can even add a simple comment to tell sonarqube not to complain about that specific case.
It seems like no-one had the problem of static AutoClosable objects, since there are no questions about it on Stack Overflow.
It's kind of a weird thing to want it, even by your customer. If there's a global variable that has this kind of state, that means your customer's code is untestable (they can't really replace it with a dummy implementation for example) - but, again, given that it's a customer, they might not be willing to hear that.
Normally you make the resource once soon after app start and hand it to the rest of your code in a try-with block that never exits unless the app exits:
public static void main(String[] args) {
try (var out = new FileOutputStream(args[0])) {
yourActualApp(out);
}
something like the above. The same would work for your library. But, your customer must want to move away from a static global field and pass the resource along everywhere to make this work, and they might not want to do that.
Split whatever java class represents your resource in twain. 2 classes, both of which simply wrap around the actual implementation. One implements AutoClosable, the other doesn't. Name the one that doesn't 'GlobalFoo' where Foo is what you currently name it - and put in its javadoc that the intent for such a thing is that it exists for the lifetime of your JVM. This way your users have to explictly opt into 'nono this one lives forever, no need to close it!', and you can add some smarts, such as - if it's a resource with a unique key (say, it's representing a file. files have unique keys: The fully qualified file name!) - you can store those unique keys and throw an error if a user of your library attempts to make such a global object using the same key more than once ever.
You can have one extend the other - that extender simply adds AutoClosable and nothing more.