Digital Edition

SYS-CON.TV
Static Initializers and Uninintializers
Static Initializers and Uninintializers

I haven't found a good discussion on the topic of class loading and unloading in my searches through Java literature or Java resources on the Web, so I thought it would be a good topic to cover this month. This month's column is all about how and when Java classes are loaded and unloaded, and how you can write classes that link into that process.

Let's start off with class loading. Java classes are loaded using objects of type java.lang.ClassLoader. Each VM has exactly one default class loader called the system class loader. Basically, when the VM detects that a particular class needs to be loaded, say a class named "Foo", the VM asks its system class loader to find "Foo". It's the class loader's job to find some resource that defines that class. It is not of the VM's concern whether that resource exists in a .CLASS file on the local file system, in a .CLASS file accessed through an HTTP server over the Internet, in a .ZIP file (or in some other location) or is even generated on-the-fly by the class loader itself. That's the class loader's concern. It's the class loaders job to find that resource somehow and load it using the method ClassLoader.defineClass().

Most system class loaders work pretty much the same way. The system class loader is aware of a system property called the classpath. The classpath is a list of local file system directories and/or .ZIP files. (And, starting with Java 1.1, it might also include .JAR files, which are a lot like .ZIP files.) Conceptually, this classpath is a list of locations to find .CLASS files.

When asked to load class "Foo", a system class loader will create the file name "Foo.class" by just sticking the extension ".class" onto the class name. The system class loader will search for this file using the entries in the classpath. For example, if the classpath contains the list of entries "A;B;C", three directory names, the system class loader will first try to find the file with the full path "A\Foo.class". Failing that, it will then look for "B\Foo.class". And finally, failing that, it will look for the file "C\Foo.class". If that third step fails, the system class loader will throw an exception of type ClassNotFoundException.

Assuming the system class loader finds the file "Foo.class" in one of the directories on the classpath, it will load the resource into the VM using ClassLoader.defineClass(). This is where things start to get interesting. The defineClass() method does several steps first to ensure the .CLASS resource is in the right format and the code in the class will not harm the VM (a process known as "verification"). Once that is done, defineClass() will create a new object of type java.lang.Class to represent the newly loaded Java class. This new object is known as the "Class object". The Class object represents the loaded class to Java code. For example, to access the Class object of any object, you can call the object's getClass() method:

// Accessing the Class object of Object "o"
Class cls = o.getClass();
String classname = cls.getName();

Getting back to class loading, Java classes can have dependencies on each other. This means that in order to load class A, it is also required to load class B. In this case, we would say class A is dependent on class B. Class dependencies are created whenever you explicitly refer to class B in class A's code. For example:

// Class Foo is dependent on class Bar
public class Foo {
public void xyz() {
Bar barObject = new Bar(); // explicit dependency
}
...
}

The explicit dependency of class Foo on class Bar means that the class Bar will automatically be loaded when it is required to be used by class Foo. These dependencies between classes are automatically detected and made to load required classes when they are needed. That is, when the method xyz() is called in class Foo, the Bar class will automatically be loaded and a corresponding Class object created.

There are several ways an explicit dependency can be set up:

  • Defining a class variable or local variable of type Bar in Foo creates a dependency of Foo on Bar.
  • Declaring a return value of type Bar in a method of class Foo creates a dependency of Foo on Bar.
  • Deriving Foo directly from class Bar creates a dependency of Foo on Bar.

    Note that there is one other step that is completed before the Class object is returned from defineClass(). That is the "static initialization" step and it happens as the last step of class loading. Each class has a static initializer block. This is a block of code that is to be run as soon as the class is loaded - before any objects of the class are created or any methods of the class called. The purpose of the static initializer block is to initialize the static members of the class and to do any other special initialization required for the class to work. Other special initializations include loading dynamic libraries that implement native code, making network or database connections that might be required, etc. Take a look at the three classes in Listing 1, which demonstrate these static initializer blocks. Both classes Foo and Bar have static initializer blocks. Class Foo is dependent on class Bar. The Main class implements a main() method (a stand-alone application), and Main is dependent on Foo because Foo is mentioned as a variable type in class Main. The static initializer blocks just print out a line of text to System.out. This is the output you would see if you ran Main using the JDK 1.1 interpreter:
    Loading class Main
    Starting the program
    Loading class Foo
    Created the Foo object
    Loading class Bar

    Starting with Java 1.1, classes could be unloaded just like they could be loaded. The rule for unloading a class is surprisingly simple and makes sense. When the Class object for a class is garbage collected, the class is automatically unloaded. Use of the garbage collector and the Class objects to represent the loaded state of a class is very elegant, understandable and intuitive.

    Note: When an explicit dependency is set up between two classes, the required class will not be unloaded until the dependent class is unloaded. There is an explicit relationship set up between the Class objects. That is, as long as the Class object for class Foo sticks around (and the Foo class is thus loaded into memory), the Class object for Bar will stick around.

    It is possible to set up "implicit" dependencies between classes. An implicit dependency means that the VM is not aware of the dependency. Instead, your program takes control of loading classes when they are required. To force the system class loader to load a class, you use the static method Class.forName (String classname). Listing 2 shows a simple class Foo which loads the class Bar using code, instead of using an explicit reference to the Bar class. This prevents an explicit relationship between Foo and Bar. In this case, it is possible for the class Bar to be loaded and unloaded, and even reloaded, several times during the life of the VM. The program in Listing 2 attempts to force the VM to garbage collect the Bar class Class object by filling memory with orphan objects every time the ENTER key is pressed by the user. Sample output from this program running in the JDK 1.1 is:

    ***Loading Foo class
    Starting the application
    ***Loading Bar class
    Filling memory with 1000 useless objects

    Filling memory with 1000 useless objects

    ***Loading Bar class
    Filling memory with 1000 useless objects
    ...

    Note that the Bar class static initializer was called twice. That is, the Bar class was unloaded at some point and then reloaded when it was later required.

    The fact that, starting with Java 1.1, classes could be unloaded, leads to an important feature actually missing from Java. While it is easy for me to write code that is called when a class is loaded (the static initializer block), there is no companion block called when a class is unloaded. The static initializer allows me to initialize static members, but there is no static uninitializer block that allows me to clean up resources.

    When would such a static uninitializer be useful? Imagine I've created a static-only class that allows me to allocate and deallocate chunks of native memory. The public interface for this NativeMemory class might look something like this:

    public class NativeMemory {
    public static int alloc(int size); // returns handle
    public static void setmem(int handle, byte[]);
    public static void dealloc(int handle);
    public static int realloc(int handle, int newsize);
    ...
    }

    For such a NativeMemory class, a static uninitializer would be crucial. Without it, there would be no way for the class to release allocated native memory that the client code forgot to deallocate.

    It would be great if you could provide a finalize() method to be used by the NativeMemory Class object. The class is unloaded when the garbage collector finalizes the Class object. This would be the perfect time for the NativeMemory class to release any native memory that hasn't been released yet. The finalize() method would, in effect, become the static uninitializer for the class.

    Unfortunately, it is not possible to change the finalize() method implementation for the Class class. Instead, however, you can delegate the responsibility of class uninitialization to another object. A reference to that object would be stored in a static variable in the NativeMemory class. This means that the clean-up object would only be garbage-collected when the NativeMemory Class object was being collected. That is, when the NativeMemory class was being unloaded. The finalize() method of the delegate object would then become the static uninitializer for the NativeMemory class.

    Listing 3 shows how a NativeMemory class could use a static reference to a delegate object to manage its static class uninitialization. The delegate object would, ideally, be a static inner class object of the NativeMemory class which would allow the delegate access to the private static member of the NativeMemory class.

    Note that, like object finalization, class unloading is not guaranteed to occur at any particular time. Better VM implementations will unload classes that haven't been used recently and that are no longer being referenced. Worse implementations may never unload unused classes. The best you can do is to write your classes with static uninitializers when required, just in case the VM does a good job unloading your class.

    So, the point of this column has been threefold:

  • To give a primer on class loading for those Java programmers who might not have a good idea of how it works
  • To explain how explicit and implicit class relationships are important
  • And something for those who already know all about class loading: How to make static uninitializers for your classes

    One final note: If your class really requires static uninitialization, even if the application quits because System.exit() is called, there is a new facility in Java 1.1 that ensures object clean up on exit. Take a look at the new System.runFinalizersOnExit() method.

    About Brian Maso
    Brian Maso is President of Blumenfeld & Maso, Inc., a Java and Media consulting company. In addition to his involvement in several commercial Java projects, he is also the author of several Java books, including Osborne/McGraw-Hill's upcoming title "Visual J++ From the Ground Up". Brian is the Java guru of DevelopMentor's Java training courses. He has also written several Java white-papers for individual
    corporations.

  • In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1



    ADS BY GOOGLE
    Subscribe to the World's Most Powerful Newsletters

    ADS BY GOOGLE

    In his general session at 19th Cloud Expo, Manish Dixit, VP of Product and Engineering at Dice, disc...
    In this presentation, you will learn first hand what works and what doesn't while architecting and d...
    SYS-CON Events announced today that CrowdReviews.com has been named “Media Sponsor” of SYS-CON's 22n...
    In his session at 20th Cloud Expo, Scott Davis, CTO of Embotics, discussed how automation can provid...
    Everyone wants the rainbow - reduced IT costs, scalability, continuity, flexibility, manageability, ...
    Founded in 2000, Chetu Inc. is a global provider of customized software development solutions and IT...
    The standardization of container runtimes and images has sparked the creation of an almost overwhelm...
    SYS-CON Events announced today that DatacenterDynamics has been named “Media Sponsor” of SYS-CON's 1...
    Most DevOps journeys involve several phases of maturity. Research shows that the inflection point wh...
    Dynatrace is an application performance management software company with products for the informatio...
    Today, we have more data to manage than ever. We also have better algorithms that help us access our...
    Andi Mann, Chief Technology Advocate at Splunk, is an accomplished digital business executive with e...
    Bill Schmarzo, author of "Big Data: Understanding How Data Powers Big Business" and "Big Data MBA: D...
    DevOpsSummit New York 2018, colocated with CloudEXPO | DXWorldEXPO New York 2018 will be held Novemb...
    DXWorldEXPO LLC announced today that ICOHOLDER named "Media Sponsor" of Miami Blockchain Event by Fi...
    @DevOpsSummit at Cloud Expo, taking place November 12-13 in New York City, NY, is co-located with 22...
    SYS-CON Events announced today that IoT Global Network has been named “Media Sponsor” of SYS-CON's @...
    To Really Work for Enterprises, MultiCloud Adoption Requires Far Better and Inclusive Cloud Monitori...
    The best way to leverage your Cloud Expo presence as a sponsor and exhibitor is to plan your news an...
    CloudEXPO New York 2018, colocated with DXWorldEXPO New York 2018 will be held November 11-13, 2018,...