ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button Java Design and Performance Optimization

Multiprocess JVMs

09/25/2001

Combining multiple Java processes into a single Java virtual machine (JVM) is one way to reduce the JVM memory overhead. This article discusses the techniques required to achieve a multiprocess JVM.

One frequent complaint about JVMs is their large memory size. JVMs typically have a several-megabyte overhead. This is acceptable if your application will itself require tens of megabytes or more, but can be annoying when you have a small application to run which should not require much in the way of memory. If you have many small applications written in Java that need to be running concurrently, such as several small monitoring or service processes, then the JVM memory overhead can rapidly add up to deplete system memory.

Several vendors of specialized JVMs, such as application servers providers, have addressed this issue by creating multiprocess JVMs. These JVMs appear to run several Java processes within one system process. There is also a (free) pure Java library for multiprocessing JVMs, Echidna. If your interest is purely in using multiprocess JVMs, without actually needing to know the technology behind them, you need read no more of this article; instead, go to the Echidna site, download the library and documentation, and make your JVM multiprocess.

The benefits of running a multiprocess JVM can be seen by running several small applications. For example, in one test, I ran four small Java applications. The multiprocess JVM required a total of four megabytes of system memory to run all four applications simultaneously. Running each application in a separate JVM required at least three megabytes per JVM, totalling over twelve megabytes of system memory. This example illustrates the advantages gained from combining small applications in one JVM when the JVM overhead is significant compared to the application memory requirements. In addition, as the JVM is already running when you start a Java process, the application startup time can be hugely reduced. Note, however, that combining applications with larger memory requirements will not gain such a large memory advantage.

Starting the main() class

To start creating our multiprocess library, the first thing the JVM needs to do is start up the class it will run. Typically, you start a class using the java executable:

java class [args...]

For the class to run, it must have a main(String[]) method defined, and the JVM will use that as the entry point to the application.

From Java code, you can start a class in the same way as the JVM does, using the reflection package (java.lang.reflect) to identify and start the class' main(String[]) method. The following method obtains a class object for the given named class, finds the main(String[]) method of that class, and starts running that main(String[]) method.

public static void startAnotherClass(
       String classname, String[] args)
throws Exception
{
  //Get the class
  Class classObject = Class.forName(classname);

  //Find the main(String[]) method of that class.
  //main has one parameter, a String array. Set
  //that argument type
  Class[] mainParamType = {args.getClass()};
  //Search for the main(String[]) method
  Method main = classObject.getMethod("main", mainParamType);
  //Create an object array of the arguments to pass
  //to the method
  Object[] mainParams = {args};

  //start the real class.
  main.invoke(null, mainParams);
}

Starting another class in its own thread

The last section showed how to start a class, but on its own doesn't give us a multiprocess JVM. We need to start the class in a separate thread, so that each time we start a class we do not block our own multiprocess starter. Once again, this is straightforward: starting the class in its own thread merely requires the creation of a new thread before calling the previously defined startAnotherClass() method. First I'll define the method that starts the separate thread:

public static Process startAnotherClassInItsOwnThread(
           String classname, String[] args)
{
  Process process = new Process(classname, args);
  Thread thread = new Thread(process);
  thread.start();
  return process;
}

I'm using an extra class called Process, which is used to hold the information needed to start the class, i.e. the classname and command line arguments (the argument list passed to the main(String[]) method). Process defines a run() method, which calls the class startup method defined in the last section. The Process class is defined as follows:

class Process {
  String theClassName;
  String[] theStartupArguments;

  public Process(String classname, String[] args)
  {
    theClassName = classname;
    theStartupArguments = args;
  }

  public void run()
  {
    startAnotherClass(theClassName, theStartupArguments);
  }
}

What about stopping processes?

We've quickly reached the stage where we can start up multiple pseudo-processes in a JVM, but what happens when you have a runaway process that you want to stop? Killing the JVM process will kill all the processes running in it, which is not ideal, so we need to have a way to kill only one specific pseudo-process. The Thread.stop() method gives us this capability. We already have a Process object, so it is straightforward to add a method to kill it: we add an instance variable to hold its thread, and a terminate() method to kill that thread. The Process class now looks as follows (new lines of code emphasized):

class Process {
  String theClassName;
  String[] theStartupArguments;
  Thread mainThread;

  public Process(String classname, String[] args)
  {
    theClassName = classname;
    theStartupArguments = args;
  }

  public void run()
  {
    mainThread = Thread.currentThread();
    startAnotherClass(theClassName, theStartupArguments);
  }

  public void terminate()
  {
    if (mainThread != null)
      mainThread.stop();
  }

}

But Thread.stop() is deprecated. Why is this, and does it affect our usage of it? Well, several methods in the Thread class are deprecated, because of potential corruption to shared data if you use those methods. Specifically, suppose you have a synchronized method which is updating multiple shared variables:

synchronized assumedAtomicUpdate()
{
  updateSomeVariables();
  anotherVariable += 42;
  yetAnotherVariable ++;
}

Comment on this articleWhat do you think of this approach?
Post your comments

Also in Java Design and Performance Optimization:

Micro-Tuning Step-by-Step

Tuning JDBC: Measuring JDBC performance

Faster List Iteration with RandomAccess Interface

Normally, this method will update all the variables in an atomic way with respect to other threads using the same monitor, i.e. every thread using the same monitor will see either all the variables updated, or none of them updated. But Thread.stop() can violate this atomicity, because the thread can be terminated right in the middle of the method, while some of the variables have a new value and others still have the old value. This can lead to a corrupt state for the application. In our case, we can assume that the pseudo-process is a separate set of classes which should not be sharing any state with other applications, so using Thread.stop() to stop the pseudo-process is relatively safe. And, in any case, there is no other reliable way to terminate pseudo-processes.

Pages: 1, 2

Next Pagearrow