Friday, May 21, 2004

Transitive Syntax
I didn't get to finish the changes today, as I got sidetracked onto other issues. The coding I was able to do is still in progress.

The previous syntax took two constraints. The first was the predicate to use surrounded by two variables, and the second defined the anchor point, using the same predicate, a single variable, and a value for the subject or object. Since the second constraint matches the first, it makes sense to remove the first. Unfortunately, that leaves only one defined variable, and this changes the whole shape of the tuples being used.

Yesterday's code had a couple of if statements and variables to handle the difference between anchored and non-anchored queries. The difference is now getting so great that I think it will be much clearer to separate the methods.

Since anchored queries still need to get the list of all statements containing a predicate, they still need to do queries based on constraints with variable subjects and predicates. This query was being provided for free with the previous syntax, but now it has to be built. We only have a single variable so we need to create the other. The name I'll use is the name of the existing variable with "_" appended. While it might make the code seem a little obtuse, it is guaranteed to have a different name.

TJ had some problems with a memory leak today. Using OptimizeIt told him that Answer objects were being kept by the RMI server. I then remembered that SR had created a server-side Set which he added all Answers into, to prevent them from being garbage collected while they were still in use. The ORA book on RMI says that UnicastRemoteObject.exportObject() will keep a reference to all exported objects, meaning that we don't need to go to special pains to prevent it from being garbage collected. I saw no equivalent statement in the Javadoc for this method, so I assumed it was true. Based on this, the set holding the objects should not be needed. Once I told TJ about this, he removed the set and found that he was using much less memory.

After further examination TJ found that sessions were suffering the same leakage problem. Sure enough, there is a Set for them as well. However, after removing this Set, a number of RMI failure errors appeared. These errors were being caused by the session objects being garbage collected, before they were finished with. This didn't make sense, as the previous set was never read, and the session objects had been exported, which should have kept them in memory.

Further investigation showed that RMI references to Answer objects were being returned as the result of a method, but this was not how RMI references to Session objects were being returned. These objects were being wrapped in an instance of a serializable class and then that object was returned. Somehow this meant that the object was not considered to be in use, and was a candidate for garbage collection.

The fix was to inform sessions of the SessionFactory object from whence they came, and to give the factory a Set of all sessions it has served out. Then during the close() method call on the Session object, UnicastRemoteObject.unexportObject() and Set.remove() were called on the object. The Set kept the reference, and dropped it when it was no longer needed.

The reason that the RMI server seems to miss these objects (and hence, allow them to be garbage collected) might be because it never saw the RMI remote references being used. The server need only keep references to registered objects that are accessible. If a remote reference is made for an object but never used, then the server should be able to clean that object up, as it is inaccessible remotely. By wrapping the remote reference in a serializable object and returning that object, the RMI server never sees the reference going over the wire, so it assumes that the reference must have been unused.

The solution would be to return the remote reference directly, or keep an internal reference to each object, in order to prevent garbage collection on them. We chose the latter, as the former would have involved significant changes to a few interfaces. The latter option just added a Set to the SessionFactory and a couple of extra methods. Having the set of active sessions also lets TJ run some metrics on the system which was another reason to go that way.

No comments: