views:

344

answers:

5

My developers and I are having an issue with objects being garbage collected in our application when we don't want them to be. We are using Java with Weblogic 10g3. We are programming a singleton pattern to handle all of our JMS connections.

There are two classes involved:

public class JMSObject {
...
private MessageProducer _producer;
private MessageConsumer _consumer;
...
// standard get/set procs... etc.
}

public class JMSFactory {
...
// Hashmap sessions with key == ConnectionFactory Name
    Hashmap<String, List<Session>> _sessions;

// Hashmap of JMSObjects with key == ConnectionFactory Name + JMS Queue Name
    Hashmap<String, List<JMSObject>> _jmsobjects;
...
// standard get/set & necessary sington functions
}

The init method of the Servlets calls the JMSFactory singlton method, any new Sessions are placed in the _sessions Hashmap and new MessageConsumer/MessageProducers are created as a JMSObject and placed in the _jmsobjects Hashmap, in the appropriate List.

The problem is that when the system is running the JMSObjects in the list get garbage collected after some time (sometimes in 5 minutes other times after a few hours.) We looked at this for a few days but could not find any reason for the JMSObjects to be garbarge collected. Since the JMSFactory has a reference to them why would the gc destroy them?

In the end we fixed it by changing the classes as follows(without changing method interfaces):

public class JMSObject {
...
private List<MessageProducer> _producers;
private List<MessageConsumer> _consumers;
...
// standard get/set procs... etc.
}

public class JMSFactory {
...
// Hashmap sessions with key == ConnectionFactory Name
    Hashmap<String, List<Session>> _sessions;

// Hashmap of JMSObjects with key == ConnectionFactory Name + JMS Queue Name
    private Hashmap<String JMSObject> _jmsobjects;
...
// standard get/set & necessary sington functions
}

So far in testing the JMSObjects are not being gc'ed. It has been running for 2 days.

Can someone explain why the indirect reference is causing the JMSObject to get gc'ed? And why the Sessions in the _sessions Hashmap was not getting gc'ed? Does it have anything to do with the fact the Sessions are built in Javax types and the JMSObject is something we wrote?

+5  A: 

Since the JMSFactory has a reference to them why would the gc destroy them?

Well, are any objects still holding reference to the JMSFactory at this point?

Typical singleton pattern keeps the reference to the singleton object in a static member:

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
        //constructor...
    }

    public static Singleton getInstance() { return instance; }
}

Is this not the pattern you are following? It is not possible to tell from the code you provided in your post, as you left out the actual singleton code...

(BTW, using Singletons for something like this sounds like it would cause pains, besides being hard to test. See Singletons Are Pathlogical Liars)

matt b
@matt b... yes things are Static, slightly different code (we instantiate the singleton in the constructor, but the variable is static). Interesting article, thanks for the link.
beggs
Well, it's a little hard to help you solve your problem if the code you are posting isn't truly the code that is having issues...
matt b
Can't actually post the commercial code... ;-) violates corporate rules.
beggs
A: 

You said the Sessions in the _sessions map were not being GC'd, but the JMSObjects were not. I doubt it's because it is something you wrote. It sounds like either the JMSFactory itself is being collected (i.e. singleton not implemented properly) or something is removing the keys from the maps. In either case, the JMSObjects would be eligible for GC, but the session objects would not because the list still has a reference to them.

Jeff Storey
+1  A: 

Without having all of the code this a tough question to solve. But there are tools to help out.

Try this link: http://blog.emptyway.com/2007/04/02/finding-memory-leaks-in-java-apps/ It provides information about using jhat and jmap. Although the article is written to find memory leaks, it provides info on how to keep track of references to an object. Maybe you can track down why your references are disappearing.

Sandro
A: 

Any chance that the classloader that loaded the JMSFactory was being unloaded, causing the JMSFactory class to be GC'ed (including the singleton instance), which frees up the HashMaps and their contents?

David Plumpton
+2  A: 

I think I know what your problem is, it's something I ran into a while back (on WebLogic 6). I believe it has to do with WebLogic's dynamic class reloading, which WebLogic seems to do from time to time, even when you're not in a development environment (I'm guessing the web.xml is somehow getting touched by some service or something).

What happened in our case was that like you, we have a single instance of an object that's defined as a static variable of some class, and just like you, it's initialized by a servlet that has it's load-on-startup parameter set. When WebLogic thinks there's a change, it reloads the webapp by garbage collecting the classloader (which is fine) but it doesn't re-initialize all the servlets that are marked "load-on-startup" (in our case, and I'm guessing yours, the servlet serves no purpose other than to initialize the static variable, there are no mappings to it, and so it cannot be invoke, the static variable gets GCed, but not-reinitialized, and the server needs to be restarted.

In our case, our solution was to initialize the static variable in a static initializer. The original developer used a servlet to initialize the variable because he wanted some servlet context information, which really wasn't necessary. If you need context information, you could try doing your initialization in a ServletContextListener.

Jack Leow
@Jack Leow; interesting, we will have to look into the behavior of the Dynamic Class Reloading. Sounds like you are saying it does not play fair. We are not seeing the problem with the newer implementation (Second code block) but still need to figure out what the root cause was. Thanks.
beggs
I see that, and that's one of the reasons I didn't respond sooner. That said, you did say that it's only been stable for a couple of days, and in our case, our system can sometimes go for weeks before exhibiting this behavior (making it a lot of fun to troubleshoot).
Jack Leow
I take it this was accepted because the second version started showing problems? :)
Jack Leow