views:

343

answers:

6

Hi,

I have read a lot about the Java class loading process lately. Often I came across texts that claimed that it is not possible to add classes to the classpath during runtime and load them without class loader hackery (URLClassLoaders etc.)

As far as I know classes are loaded dynamically. That means their bytecode representation is only loaded and transformed to a java.lang.Class object when needed.

So shouldn't it be possible to add a JAR or *.class file to the classpath after the JVM started and load those classes, provided they haven't been loaded yet? (To be clear: In this case the classpath is simply folder on the filesystem. "Adding a JAR or *.class file" simply means dropping them in this folder.)

And if not, does that mean that the classpath is searched on JVM startup and all fully qualified names of the found classes are cached in an internal "list"?

It would be nice of you if you could point me to some sources in your answers. Preferably the offical SUN documentation: Sun JVM Spec. I have read the spec but could not find anything about the classpath and if it's finalized on JVM startup.

P.s.

This is a theoretical question. I just want to know if it is possible. There is nothing practical I want to achieve. There is just my thirst for knowledge :)

A: 

I can only comment from what I remember of my own experience of hacking a non-sun JVM ten years ago, but it did scan the entire classpath on startup, as an efficiency measure. The names of all classes found were added to an internal hashtable along with their locations (directory or zip/jar file). However, that was ten years ago and I can't help but wonder whether this would still be a reasonable thing to do in most settings considering how disk and memory architectures have evolved.

crazyscot
I think so too. I am relatively new to Java and would be surprised if the classpath would indeed only be "scanned" on startup. This just sounds wrong. Or there is a good reason why it's done that way. But so far thank you for your answer!
Jens
+2  A: 

See this discussion Modify Classpath At Runtime

stacker
That method works because the system class loader is a subclass of `URLClassLoader`. Note that there is no guarantee that this is always so (it's undocumented), so relying on this might make your program not work on a future version of Java. In some environments (for example in a Java EE app server) it might also not work.
Jesper
A: 

I believe that the classpath is taken to be static and the result of changing the files is undefined.

If you really want to be able to add and remove classes at runtime, consider doing so in your own classloader. This is what web containers do.

Thorbjørn Ravn Andersen
A: 

So shouldn't it be possible to add a JAR or *.class file to the classpath after the JVM started ...

You add jars and directories to the classpath, not classes. The classes are in either the directory, or the jar.

And if not, does that mean that the classpath is searched on JVM startup and all fully qualified names of the found classes are cached in an internal "list"?

That could be easily tested: Set the classpath, start your program, move a new class into the CP, call 'Class.forName ("NewClass") from your program. Does it find the new class?

user unknown
+1  A: 

There are two concepts here that are being intermixed: The classpath and the class files in the classpath.

If you point the classpath to a directory, you will generally have no issue adding a file to the directory and having it picked up as part of the classpath. Due to the potential size of all classes in the classpath it isn't really feasible for a modern JVM to load them all at startup. However this is of limited value as it will not include Jar files.

However, changing the classpath itself (which directories, jars, etc. are searched) on a running JVM will depend very much on the implementation. As far as I know, on standard Sun JVMs there is no documented (as in guaranteed to work) method of accomplishing this.

In general, if this is something you need to do (have a dynamic classpath that changes at runtime) then you want to be implementing a ClassLoader, if for no other reason than to be able to throw it away and make a new one that doesn't reference those classes anymore if they need to be unloaded.

However, for a small amount of dynamic loading there are better ways. In Java 1.6 you can specify all the jar files in a directory (*.jar) so you can tell users to put additional libraries in a specified location (although they have to be there at startup).

You also have the option of including a jar file or other location in the classpath even though you don't need it, as a placeholder for someone to put an optional jar or resource file there (such as a log configuration file).

But if you need serious dynamic class loading and especially unloading while the application is running, a Classloader implementation is required.

Yishai
+5  A: 

Since nobody could give my a definite answer nor a link to a corresponding part of the documentation I provide a answer myself. Nevertheless I would like to thank everybody that tried to answer the question.

Short answer:

The classpath is not final upon JVM start.

You actually can put classes in the classpath after the JVM started and they will be loaded.


Long answer:

To answer this question I went after user unknowns suggestion and wrote a little test program.

The basic idea is to have two classes. One is the main class which instantiates the second class. On startup the second class is not on the classpath. After the cli program started it'll prompt you to press enter. Before you press enter you copy the second class on the classpath. After you press enter the second class is instantiated. If the classpath would be final on JVM startup this would throw an Exception. But it doesn't. So I assume the classpath is not final on JVM startup.

Here are the source codes:

JVMTest.java

package jvmtest;

import java.io.Console;
import jvmtest.MyClass;

public class JVMTest {
  public static void main(String[] args) {
    System.out.println("JVMTest started ...");

    Console c = System.console();
    String enter = c.readLine("Press Enter to proceed");
    MyClass myClass = new MyClass();
    System.out.println("Bye Bye");
  }
}

MyClass.java

package jvmtest;

public class MyClass {
  public MyClass() {
    System.out.println("MyClass v2");
  }
}

The folder structure looks like this:

jvmtest/
  JVMTest.class
  MyClass.class

I started the cli program with this command:

> java -cp /tmp/ jvmtest.JVMTest

As you can see I had my jvmtest folder in /tmp/jvmtest. You obviously have to change this according to where you put the classes.

So here are the steps I performed:

  1. Make sure only JVMTest.class is in jvmtest.
  2. Start the program with the command from above.
  3. Just to be sure press enter. You should see an Exception telling you that no class could be found.
  4. Now start the program again.
  5. After the program started and you are prompted to press enter copy the MyClass file into the jvmtest folder.
  6. Press enter. You should see "MyClass v1".

Additional notes:

This also worked when I packed the MyClass class in a jar and run the test above.

I ran this on my Macbook Pro running Mac OS X 10.6.3

> Java -version

results in:

java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)
Jens