This is a tricky problem. Are you able to replicate this on a test system?
In the following I describe how I would approach solving your problem given the information you have provided.
First verify that this is actually the problem, by using a profiler on a system showing the problem while running. The traditional start is to use the stand alone version of the Netbeans profiler - VisualVMM (https://visualvm.github.io/) - where it is easy to see the memory usage over time, and to enable object allocation insights. I am unsure if the current version of VisualVM can tell you where a given object was allocated, but if it can then that might be the fastest way to identify your problematic code. If not then a trial version of a profiler that can, might be worth looking into.
If the profiler confirms that "calling X.openConnection() and then not calling X.closeConnection()" somewhere is the problem, but you still need to find out exactly where and tools like Findbugs - http://findbugs.sourceforge.net/ - and PMD - https://pmd.github.io/ - does not help you (a bit of effort here goes a long way), then I would suggest changing your code so that you collect the information you need.
If you are actually talking about database connections, then this is not an uncommon problem, so tools exist to help you. There is a rather old question at How to check the Database Connection leakage in Java EE application? but which has suggestions on how to approach this. It also has suggestions on how to catch this with tests.
At this point I would look into code - please consider the following non-compilable pseudocode. Write a revised subclass of X which maintains a global
var openConnections = new ArrayList<Map<X, Exception>>():
where each call to openConnection()
also adds a single element with the object and a corresponding exception to a list somewhat like this:
public ... openConnection(...) {
openConnections.add(Map.of(this, new Exception()));
return super.openConnection();
}
and then each call to closeConnection()
removes it again.
public ... closeConnection(...) {
// Loop over openConnections, if the current map has this as the first key, then
// delete that entry.
return super.closeConnection();
}
You should then at any time be able to see your open connections in the debugger, and where they were invoked from.
If you have the time I would strongly suggest that you rewrite your code to conform to the AutoCloseable interface instead as it allows you to use try-with-resources to let the compiler help you, and hints to static code analysis tools that this is what you want to do every time. See implements Closeable or implements AutoCloseable for a good starter question with answers.