Monday, 26 September 2011

Garbage-First Collector


What is the Garbage-First Collector?


Garbage-First (G1) Collector is a server-style garbage collector, targeted for multi-processors with large memories, that meets a soft real-time goal with high probability, while achieving high throughput. G1 is the long term replacement of the Concurrent Mark-Sweep Collector (CMS). Whole-heap operations, such as global marking, are performed concurrently with the application threads, to prevent interruptions proportional to heap or live-data size. Concurrent marking provides both collection "completeness" and identifies regions ripe for reclamation via compacting evacuation. This evacuation is performed in parallel on multi-processors, to decrease pause times and increase throughput.
The G1 collector achieves these goals through several techniques. The heap is partitioned into a set of equal-sized heap regions, each a contiguous range of virtual memory. G1 compacts as it proceeds. It copies objects from one area of the heap to the other. Thus, it will not encounter fragmentation issues that CMS might encounter. There will always be areas of contiguous free space from which to allocate, allowing G1 to have consistent pauses over time. G1 uses a pause prediction model to meet a user-defined pause time target. It achieves smoother pause times than CMS at comparable throughput.
After G1 performs a global marking phase determining the liveness of objects throughout the heap, it will immediately know where in the heap there are regions that are mostly empty. It will tackle those regions first, potentially making a lot of space available. This way, the garbage collector will obtain more breathing space, decreasing the probability of a full GC. This is also why the garbage collector is called Garbage-First. As its name suggests, G1 concentrates its collection and compaction activity first on the areas of the heap that are likely to be full of reclaimable objects, thus improving its efficiency.
While most real-time collectors work at the highly granular level of individual objects, G1 collects at the region level. If any region contains no live objects it is immediately reclaimed. The user can specify a goal for the pauses and G1 will do an estimate of how many regions can be collected in that time based on previous collections. So, the collector has a reasonably accurate model of the cost of collecting the regions, and therefore "the collector can choose a set of regions that can be collected within a given pause time limit with high probability." In other words, G1 is not a hard real-time collector - it meets the soft real-time goal with high probability but not absolute certainty. G1 attempts to yield higher throughput in return for the softer, but still moderately stringent, real-time constraints. This is a good fit for large-scale server applications which often have large amounts of live heap data and considerable thread-level parallelism. G1 also provides some finer control, allowing a user to specify a fraction of time during a period of execution to be spent on garbage collection. For example, for every 250ms of execution spend no more than 50ms on garbage collection.
G1 is planned to replace CMS in the Hotspot JVM. There are two major differences between CMS and G1. The first is that G1 is a compacting collector. G1 compacts sufficiently to completely avoid the use of fine-grain free lists for allocation, which considerably simplifies parts of the collector and mostly eliminates potential fragmentation issues. As well as compacting, G1 offers more predictable garbage collection pauses than the CMS collector and allows users to set their desired pause targets.

Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking


Handling More Than One Type of Exception

In Java SE 7 and later, a single catch block can handle more than one type of exception. This feature can reduce code duplication and lessen the temptation to catch an overly broad exception.
Consider the following example, which contains duplicate code in each of the catch blocks:
catch (IOException ex) {
     logger.log(ex);
     throw ex;
catch (SQLException ex) {
     logger.log(ex);
     throw ex;
}
In releases prior to Java SE 7, it is difficult to create a common method to eliminate the duplicated code because the variable ex has different types.
The following example, which is valid in Java SE 7 and later, eliminates the duplicated code:
catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}
The catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar (|).
Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter exis final and therefore you cannot assign any values to it within the catch block.
Bytecode generated by compiling a catch block that handles multiple exception types will be smaller (and thus superior) than compiling manycatch blocks that handle only one exception type each. A catch block that handles multiple exception types creates no duplication in the bytecode generated by the compiler; the bytecode has no replication of exception handlers.

Rethrowing Exceptions with More Inclusive Type Checking

The Java SE 7 compiler performs more precise analysis of rethrown exceptions than earlier releases of Java SE. This enables you to specify more specific exception types in the throws clause of a method declaration.
Consider the following example:
static class FirstException extends Exception { }
  static class SecondException extends Exception { }

  public void rethrowException(String exceptionName) throws Exception {
    try {
      if (exceptionName.equals("First")) {
        throw new FirstException();
      } else {
        throw new SecondException();
      }
    } catch (Exception e) {
      throw e;
    }
  }
This examples's try block could throw either FirstException or SecondException. Suppose you want to specify these exception types in thethrows clause of the rethrowException method declaration. In releases prior to Java SE 7, you cannot do so. Because the exception parameter of the catch clause, e, is type Exception, and the catch block rethrows the exception parameter e, you can only specify the exception type Exceptionin the throws clause of the rethrowException method declaration.
However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in therethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException. Even though the exception parameter of the catch clause, e, is type Exception, the compiler can determine that it is an instance of either FirstException orSecondException:
public void rethrowException(String exceptionName)
  throws FirstException, SecondException {
    try {
      // ...
    }
    catch (Exception e) {
      throw e;
    }
  }
This analysis is disabled if the catch parameter is assigned to another value in the catch block. However, if the catch parameter is assigned to another value, you must specify the exception type Exception in the throws clause of the method declaration.
In detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catchblock, the compiler verifies that the type of the rethrown exception meets the following conditions:
  • The try block is able to throw it.
  • There are no other preceding catch blocks that can handle it.
  • It is a subtype or supertype of one of the catch clause's exception parameters.
The Java SE 7 compiler allows you to specify the exception types FirstException and SecondException in the throws clause in therethrowException method declaration because you can rethrow an exception that is a supertype of any of the types declared in the throws.
In releases prior to Java SE 7, you cannot throw an exception that is a supertype of one of the catch clause's exception parameters. A compiler from a release prior to Java SE 7 generates the error, "unreported exception Exception; must be caught or declared to be thrown" at the statement throw e. The compiler checks if the type of the exception thrown is assignable to any of the types declared in the throws clause of the rethrowExceptionmethod declaration. However, the type of the catch parameter e is Exception, which is a supertype, not a subtype, of FirstExceptionandSecondException.

The try-with-resources Statement


The try-with-resources statement is a try statement that declares one or more resources. A resource is as an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
The following example reads the first line from a file. It uses an instance of BufferedReader to read data from the file. BufferedReader is a resource that must be closed after the program is finished with it:
static String readFirstLineFromFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}
In this example, the resource declared in the try-with-resources statement is a BufferedReader. The declaration statement appears within parentheses immediately after the try keyword. The class BufferedReader, in Java SE 7 and later, implements the interfacejava.lang.AutoCloseable. Because the BufferedReader instance is declared in a try-with-resource statement, it will be closed regardless of whether the try statement completes normally or abruptly (as a result of the method BufferedReader.readLine throwing an IOException).
Prior to Java SE 7, you can use a finally block to ensure that a resource is closed regardless of whether the whether the try statement completes normally or abruptly. The following example uses a finally block instead of a try-with-resources statement:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
  BufferedReader br = new BufferedReader(new FileReader(path));
  try {
    return br.readLine();
  } finally {
    if (br != null) br.close();
  }
}
However, in this example, if the methods readLine and close both throw exceptions, then the methodreadFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed. In contrast, in the example readFirstLineFromFile, if exceptions are thrown from both the try block and the try-with-resources statement, then the method readFirstLineFromFile throws the exception thrown from the try block; the exception thrown from the try-with-resources block is suppressed. In Java SE 7 and later, you can retrieve suppressed exceptions; see the section Suppressed Exceptions for more information.
You may declare one or more resources in a try-with-resources statement. The following example retrieves the names of the files packaged in the zip file zipFileName and creates a text file that contains the names of these files:
public static void writeToFileZipFileContents(String zipFileName, String outputFileName)
    throws java.io.IOException {

    java.nio.charset.Charset charset = java.nio.charset.Charset.forName("US-ASCII");
    java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName);

    // Open zip file and create output file with try-with-resources statement

    try (
      java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
      java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {

      // Enumerate each entry

      for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {

        // Get the entry name and write it to the output file

        String newLine = System.getProperty("line.separator");
        String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
        writer.write(zipEntryName, 0, zipEntryName.length());
      }
    }
  }
In this example, the try-with-resources statement contains two declarations that are separated by a semicolon: ZipFile and BufferedWriter. When the block of code that directly follows it terminates, either normally or because of an exception, the close methods of the BufferedWriterand ZipFile objects are automatically called in this order. Note that the close methods of resources are called in the opposite order of their creation.
The following example uses a try-with-resources statement to automatically close a java.sql.Statement object:
public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) {

      ResultSet rs = stmt.executeQuery(query);

      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + ", " + supplierID + ", " + price +
                           ", " + sales + ", " + total);
      }

    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
  }
The resource java.sql.Statement used in this example is part of the JDBC 4.1 and later API.
Note: A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.

Suppressed Exceptions

An exception can be thrown from the block of code associated with the try-with-resources statement. In the examplewriteToFileZipFileContents, an exception can be thrown from the try block, and up to two exceptions can be thrown from the try-with-resources statement when it tries to close the ZipFile and BufferedWriter objects. If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception thrown by the block is the one that is thrown by the writeToFileZipFileContents method. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.

Classes That Implement the AutoCloseable or Closeable Interface

See the Javadoc of the AutoCloseable and Closeable interfaces for a list of classes that implement either of these interfaces. The Closeableinterface extends the AutoCloseable interface. The close method of the Closeable interface throws exceptions of type IOException while theclose method of the AutoCloseable interface throws exceptions of type Exception. Consequently, subclasses of the AutoCloseable interface can override this behavior of the close method to throw specialized exceptions, such as IOException, or no exception at all.

Underscores in Numeric Literals


In Java SE 7 and later, any number of underscore characters (_) can appear anywhere between digits in a numerical literal. This feature enables you, for example, to separate groups of digits in numeric literals, which can improve the readability of your code.
For instance, if your code contains numbers with many digits, you can use an underscore character to separate digits in groups of three, similar to how you would use a punctuation mark like a comma, or a space, as a separator.
The following example shows other ways you can use the underscore in numeric literals:
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi =  3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
You can place underscores only between digits; you cannot place underscores in the following places:
  • At the beginning or end of a number
  • Adjacent to a decimal point in a floating point literal
  • Prior to an F or L suffix
  • In positions where a string of digits is expected
The following examples demonstrate valid and invalid underscore placements (which are highlighted) in numeric literals:
float pi1 = 3_.1415F;      // Invalid; cannot put underscores adjacent to a decimal point
float pi2 = 3._1415F;      // Invalid; cannot put underscores adjacent to a decimal point
long socialSecurityNumber1
  = 999_99_9999_L;         // Invalid; cannot put underscores prior to an L suffix

int x1 = _52;              // This is an identifier, not a numeric literal
int x2 = 5_2;              // OK (decimal literal)
int x3 = 52_;              // Invalid; cannot put underscores at the end of a literal
int x4 = 5_______2;        // OK (decimal literal)

int x5 = 0_x52;            // Invalid; cannot put underscores in the 0x radix prefix
int x6 = 0x_52;            // Invalid; cannot put underscores at the beginning of a number
int x7 = 0x5_2;            // OK (hexadecimal literal)
int x8 = 0x52_;            // Invalid; cannot put underscores at the end of a number

int x9 = 0_52;             // OK (octal literal)
int x10 = 05_2;            // OK (octal literal)
int x11 = 052_;            // Invalid; cannot put underscores at the end of a number

Strings in switch Statements


In the JDK 7 release, you can use a String object in the expression of a switch statement:
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
     String typeOfDay;
     switch (dayOfWeekArg) {
         case "Monday":
             typeOfDay = "Start of work week";
             break;
         case "Tuesday":
         case "Wednesday":
         case "Thursday":
             typeOfDay = "Midweek";
             break;
         case "Friday":
             typeOfDay = "End of work week";
             break;
         case "Saturday":
         case "Sunday":
             typeOfDay = "Weekend";
             break;
         default:
             throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
     }
     return typeOfDay;
}
The switch statement compares the String object in its expression with the expressions associated with each case label as if it were using theString.equals method; consequently, the comparison of String objects in switch statements is case sensitive. The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

Java™ SE 7 Release Security Enhancements


The Java SE 7 release adds the following features:
  • Elliptic Curve Cryptography (ECC)
    A new native provider has been added to the Java SE 7 release that provides several ECC-based algorithms (ECDSA/ECDH). See Sun PKCS#11 Provider's Supported Algorithms in Java PKCS#11 Reference Guide for more information.

  • CertPath Algorithm Disabling
    Weak cryptographic algorithms can now be disabled. For example, the MD2 digest algorithm is no longer considered secure. The Java SE 7 release provides a mechanism for denying the use of specific algorithms in certification path processing and TLS handshaking. See Appendix D: Disabling Cryptographic Algorithms in Java PKI Programmer's Guide and Disabled Cryptographic Algorithms in Java Secure Socket Extension (JSSE) Reference Guide for more information.

  • JSSE (SSL/TLS)
    • TLS 1.1
      The SunJSSE provider now supports TLS 1.1 as described in RFC 4346. The most important update is protection against cipher block chaining (CBC) attacks.

    • TLS 1.2
      The SunJSSE provider now supports TLS 1.2 as described in RFC 5246. Among other things, it specifies different internal hashing algorithms, adds new cipher suites, and contains improved flexibility, particularly for negotiation of cryptographic algorithms.

    • Weak cipher suites deprecated
      Per RFC 4346RFC 5246, and RFC 5469, some cipher suites have been made obsolete and should not be used. These obsolete suites are all disabled by default in SunJSSE. For details, consult the cipher suite lists in the documentation about the SunJSSE provider.

    • Connection-sensitive trust management
      Both trust managers and key managers now have the ability to examine parameters of the TLS connection, specifically theSSLSession under construction, during the handshake. For example, a trust manager might restrict the types of certificates used based on the list of valid signature algorithms.

    • Endpoint verification
      An endpoint identification algorithm can be specified to verify that a remote computer's host address matches its supplied certificate. Although this type of verification was previously performed for the HTTPS protocol (see HttpsURLConnection andHostnameVerifier), such verification can now be optionally performed at the TLS level.

    • TLS renegotiation
      Java SE supports RFC 5746, which fixes a renegotiation issue in the TLS protocol.

    • SSLv2Hello disabled by default
      In Java SE 7, SSLv2Hello is removed from the default enabled protocol list.

    • Algorithm disabling
      Weak cryptographic algorithms can now be disabled, as previously described.

    • Server Name Indication (SNI) for JSSE client
      The Java SE 7 release supports the Server Name Indication (SNI) extension in the JSSE client. SNI is described in RFC 4366. This enables TLS clients to connect to virtual servers.

    • Tighter checking of EncryptedPreMasterSecret version numbers
      Java SE 7 tightens version number checking during TLS 1.1 and TLS 1.2 handshaking. See the JSSE Reference Guide for more information.


Enhancements in Java I/O


The java.nio.file package and its related package, java.nio.file.attribute, provide comprehensive support for file I/O and for accessing the file system. A zip file system provider is also available in JDK 7. The following resources provide more information:

Enhancements in Java SE 6

java.io

One new class is provided:
  • Console - Contains methods to access a character-based console device. The readPassword() methods disable echoing thus they are suitable for retrieval of sensitive data such as passwords. The method System.console() returns the unique console associated with the Java Virtual Machine.
The following new methods were added to File:
Constructors were added to the following class:
The behavior of the following method was modified:
  • The File.isFile() Windows implementation has been modified to always return false for reserved device names such as CON, NUL, AUX, LPT, etc. Previously it returned true, which customers considered a bug because it was inconsistent with behavior for devices on Unix.

java.nio

  • A new java.nio.channels.SelectorProvider implementation that is based on the Linux epoll event notification facility is included. The epoll facility is available in the Linux 2.6, and newer, kernels. The new epoll-based SelectorProvider implementation is more scalable than the traditional poll-based SelectorProvider implementation when there are thousands of SelectableChannels registered with aSelector. The new SelectorProvider implementation will be used by default when the 2.6 kernel is detected. The poll-basedSelectorProvider will be used when a pre-2.6 kernel is detected.
  • The system property sun.nio.ch.disableSystemWideOverlappingFileLockCheck controls whetherjava.nio.channels.FileChannel.lock() checks whether regions are locked by other instances of FileChannel. Unless this system property is set to trueFileChannel.lock() will throw an OverlappingFileLockException if an application attempts to lock a region that overlaps one that is locked by another instance of FileChannel. The system property exists to provide compatibility with previous releases which do not implement the JVM-wide overlapping file lock check.

Enhancements in J2SE 5.0

java.nio

  • Class javax.net.ssl.SSLEngine was added. The existing J2SDK 1.4.0 secure communication infrastructure, javax.net.ssl.SSLSocket, was based on the I/O model of java.net.Socket. The SSLEngine class abstracts away the I/O model, and leaves developers free to secure communication pathways using alternate types of I/O. SSLEngine can be combined with the New I/O APIs to create implementations such as secure blocking or non-blocking SocketChannels. For details see the JSSE Reference Guide.

Enhancements in Java 2 SDK v1.4

java.io

  • In the FileInputStream and FileOutputStream classes, getChannel methods have been added to return the underlying FileChannelobject, and the close methods have been revised to close the underlying channel.
  • Similarly, in the RandomAccessFile class, a getChannel method has been added and the close method has been revised to close the underlying channel. The RandomAccessFile constructors now support the mode characters "s" and "d" to allow the specification, at file-open time, of synchronous writes or synchronous-data writes.
  • In the InputStreamReader and OutputStreamWriter classes, constructors have been added that take a stream and a Charset object, and the getEncoding methods have been revised to return the historical name of the encoding being used. Constructors to InputStreamReaderand OutputStreamWriter have been added that take existing CharsetDecoder and CharsetEncoder objects, respectively.

java.nio

  • The nio packages were added to supplement the I/O facilities provided the java.io package.
  • Three new functions were added to the Java Native Interface (JNI) to support direct buffers. For information, see JNI Enhancements in v 1.4.

Previous Enhancements

Networking Enhancements in Java SE 7


Networking Enhancements in Java SE 7

The URLClassLoader.close method has been added. This method effectively eliminates the problem of how to support updated implementations of the classes and resources loaded from a particular codebase, and in particular from JAR files. 
Details of URLClassLoader.close():-->

Constructor Summary

URLClassLoader(URL[] urls)
Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
URLClassLoader(URL[] urls, ClassLoader parent)
Constructs a new URLClassLoader for the given URLs.
URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)
Constructs a new URLClassLoader for the specified URLs, parent class loader, and URLStreamHandlerFactory.

Method Summary

Modifier and TypeMethod and Description
protected voidaddURL(URL url)
Appends the specified URL to the list of URLs to search for classes and resources.
voidclose()
Closes this URLClassLoader, so that it can no longer be used to load new classes or resources that are defined by this loader.
protected PackagedefinePackage(String name, Manifest man, URL url)
Defines a new package by name in this ClassLoader.
protected Class<?>findClass(String name)
Finds and loads the class with the specified name from the URL search path.
URLfindResource(String name)
Finds the resource with the specified name on the URL search path.
Enumeration<URL>findResources(String name)
Returns an Enumeration of URLs representing all of the resources on the URL search path having the specified name.
protected PermissionCollectiongetPermissions(CodeSource codesource)
Returns the permissions for the given codesource object.
InputStreamgetResourceAsStream(String name)
Returns an input stream for reading the specified resource.
URL[]getURLs()
Returns the search path of URLs for loading classes and resources.
static URLClassLoadernewInstance(URL[] urls)
Creates a new instance of URLClassLoader for the specified URLs and default parent class loader.
static URLClassLoadernewInstance(URL[] urls, ClassLoader parent)
Creates a new instance of URLClassLoader for the specified URLs and parent class loader.

URLClassLoader

public URLClassLoader(URL[] urls)
Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader. The URLs will be searched in the order specified for classes and resources after first searching in the parent class loader. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be downloaded and opened as needed.If there is a security manager, this method first calls the security manager's checkCreateClassLoader method to ensure creation of a class loader is allowed.
Parameters:
urls - the URLs from which to load classes and resources
Throws:
SecurityException - if a security manager exists and its checkCreateClassLoader method doesn't allow creation of a class loader.
See Also:
SecurityManager.checkCreateClassLoader()

Closing a URLClassLoader

How to Close a URLClassLoader?

The URLClassLoader close() method effectively eliminates the problem of how to support updated implementations of the classes and resources loaded from a particular codebase, and in particular from JAR files. In principle, once the application clears all references to a loader object, the garbage collector and finalization mechanisms will eventually ensure that all resources (such as the JarFile objects) are released and closed.
The application can then replace the JAR file, and create a new URLClassLoader instance to load from the same location, but this time using the new implementation of the classes/resources. However, since it can't be predicted exactly when finalization and garbage collection will occur, problems are caused for applications which need to be able to do this in a predictable and timely fashion. It is a problem on Windows, because open files cannot be deleted or replaced.
In Java SE 7, the URLClassLoader close() method effectively invalidates the loader, so that no new classes can be loaded from it. It also closes any JAR files that were opened by the loader. This allows the application to delete or replace these files and, if necessary, create new loaders using new implementations.
The close() method follows the familiar "Closeable" pattern, and URLClassLoader now implements the Closeable interface, which defines URLClassLoader.close(). The following sample code shows how one might use the method.
       //
       // create a class loader loading from "foo.jar"
       //
       URL url = new URL("file:foo.jar");
       URLClassLoader loader = new URLClassLoader (new URL[] {url});
       Class cl = Class.forName ("Foo", true, loader);
       Runnable foo = (Runnable) cl.newInstance();
       foo.run();
       loader.close ();

       // foo.jar gets updated somehow

       loader = new URLClassLoader (new URL[] {url});
       cl = Class.forName ("Foo", true, loader);
       foo = (Runnable) cl.newInstance();
       // run the new implementation of Foo
       foo.run();