Linkage and class name resolution

Short version: Can I omit a class from a JAR file (knowing that that class is never actually used) without the JVM throwing a NoClassDefFoundError exception?

Longer version: My game includes within it a couple of mini-games that I’ve set up so that they can be played as stand-alone games. Now, the JAR file for the full game is quite big, so I’m thinking that for the stand-alone mini-games I should be able to put them in much smaller JAR files by omitting the unused classes. Trouble is, I can’t simply omit classes from the JAR (classes that are mentioned, but that I know are never actually used) because the JVM still tries to load them.

A bit of reading up (http://java.sun.com/docs/books/jls/third_edition/html/execution.html) tells me that when the JVM loads a new class, it is optional whether or not it resolves all of the other class names mentioned by the loaded class, and how deep it goes in recursing this. In principle, a JVM could complete the recursion and load up every class mentioned in the JAR as soon as it is launched (“static resolution”).

So, my question is whether it’s possible to tell the JVM not to load a class (and, specifically, not to throw a NoClassDefFoundError if the class cannot be loaded) until that class is explicitly used.

(I had thought it was possible to do this by calling the Class.forName(“MyClass”) method, and catching the exception at that point, but that doesn’t seem to stop the JVM still trying to load the class at a later time.)

Does this make sense to anyone, or am I talking complete gibberish? :slight_smile:

Thanks for any suggestions!
Simon

I don’t really understand VM anything better then you do, but if VM tries to load them that means some of your other classes are using (or just importing) them ? I can’t see how they could be loaded if they aren’t used.

Mini example:


if ( game == SpaceInvaders ) {
  return new Invader();
else if ( game == Pacman ) {
  return new Ghost();
}

If you’re playing Space Invaders, you never need to instantiate a Ghost object. So an Invaders-only version of the JAR file could omit the Ghost class altogether, right?

Sadly not, because the JVM can look at the above code as soon as the game is launched and decide it needs to load both the Invader class and the Ghost class immediately. No Ghost class means NoClassDefFoundError.

So I’m looking for a way to tell the JVM not to complain about the missing Ghost class until it tries to execute the code that instantiates that class (which, if we’re playing Space Invaders, should never happen).

Bit of an odd example (it screams out “factory pattern”!) but hopefully a bit clearer than before.
Simon

Basically, you’d have create an interface for the mini game and when needed instanciate it with reflection.

MiniGame g = (MiniGame) Class.forName(“package.MiniGameImpl”);

like that your only prerequisite for the jar is the interface, hopefully much lighter than the full class and linked subclasses.

Lilian :slight_smile:

So Simon, you won’t / can’t edit your source and just remove code that uses other classes? I can’t really complain about JVM behaving that way becouse if it wouldn’t there would be much more problems then comfort in cases like this.
My next try would be to create empty classes with same name and replace the originals so JVM would not complain about not having them around.

EDIT:
about your example… are you sure it is that way? How about really creating one and test it, becouse in my game I use mysql jdbc driver to connect to database. If don’t include mysql jar file my game starts fine, and only complains about missing class file when I actually run part of game that communicates with database.

java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at viktorije.ViktorijeDatabase.DBconnect(ViktorijeDatabase.java:29)
at viktorije.ViktorijeDatabase.DBaddToServerList(ViktorijeDatabase.java:78)
at viktorije.ViktorijePanel.addToServerList(ViktorijePanel.java:1321)
at viktorije.ActionHandler.mousePressed(ActionHandler.java:198)
at java.awt.Component.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Window.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

[quote] at java.lang.Class.forName(Unknown Source)
[/quote]
reflextively loaded. it can’t predict reflexion so it can’t resolve stuff this has cons and pro’s appearandly.

anyways, I’d create stubs make sure your dependies are alrightyou could alo do it reflextively abuse the exception to check if the game is there to allow access to it

Thanks for the suggestions, folks!

Yes, the obvious solution would be to comment out the offending lines - but that’s a bit of a one-off hack, and I’d prefer to put something more permanent in place.

I agree that the “correct” solution would be to refactor the code to use trivial base classes, and to have the proper classes created by reflection if they’re actually being used in the game. But to put things into perspective, all I’m trying to do here is make a JAR file smaller. Call me lazy, but refactoring the code to achieve this seems a little disproportionate. :wink:

So in the end I’m probably just going to live with the fact that the JAR is rather larger than it needs to be.

BUT… Can anyone explain to me why the following code works? It’s from http://www.java-gaming.org/forums/index.php?topic=5488.15.

webstart = true;
try {
    Class.forName("javax.jnlp.ServiceManager");
    // this causes to go and see if the service is available
    ServiceManager.lookup("javax.jnlp.PersistenceService");
} catch (Exception e) {
    System.err.println("MuffinProvider: No webstart detected");
    webstart = false;
}

The code provides Muffin-like functionality (persistent storage) whether or not it’s running under WebStart. The thing is, if we’re not running under WebStart, then the class javax.jnlp.ServiceManager doesn’t exist (and so Class.forName() throws an exception). But somehow the code is still allowed to to refer explicitly to the ServiceManager class. Why doesn’t the JVM try to load that class when it is launched?

Cheers,
Simon

You never get to ServiceManager since exception is thrown (and you catch it). When exception is thrown thread execution halts (and is resumed in catch() if there is one).

Yes, and that’s exactly the behaviour I’m looking for! :slight_smile: The ServiceManager class doesn’t exist (unless you’re running under WebStart) but the JVM doesn’t seem unhappy that its name is mentioned in the code. So how come it won’t let me run a JAR file that omits some classes that are never used? What am I missing?
Simon

If you read the JLS; in particular :-

http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html

all your questions will be answered.

you don’t load classes reflexively (or whatever mr_light said). When you do usual class creation existence of those classes is checked compile / start time (?) and you get error instantly if they are missing. When you load classes using Class.forName() then they are reflextively loaded, as I can understand from reply of mr_light, and JVM checks if it exist at runtime at that line, so you can catch exception JVM throws if class is missing. This is first time I hear about it so I can’t tell you anything besides this :slight_smile:

Thanks for the pointer. One read-through hasn’t been enough to answer my question, but I’ll keep trying!
Simon

There is a right way and a wrong way to do this:

right: game is a public static final boolean

wrong: anything else that lacks the final part (IIRC)

If you do it the right way, you have just guaranteed to the compiler that game will never be anything else and everything will Just Work. If you do it the wrong way, you’ve just told the compiler that it could be anything, and therefore it can/should try to load everything.

Modulo some more complex / clever tricks you can do around this, which I don’t think you’re interested in. I.e. the first question is simply: have you tried setting game to be final and static?

Aha! Yes, that’s the sort of trick I was looking for. Thanks!

I’ve added a public static final flag that protects the bits of my code that refer to non-minigame classes from inside the minigame, and when I switch that on the JVM makes no attempt to load those non-existent classes. Exactly as you say!

So that’s less code for me to upload to JGF now. :wink:

Cheers,
Simon