JAVA: Loading and unloading static fields.

Overview

  • Static fields live on the heap, can have any number of copies and are cleaned up by the GC like any other object.
  • Loading multiple copies of a static field:
    • Each class loader which loads a class has its own copy of static fields.
    • If you load a class in two different class loaders these classes can have static fields with different values.
  • Unloading static fields
    • Static fields are unloaded when the Class’ ClassLoader is unloaded.
    • This is unloaded when a GC is performed and there are no strong references from the threads’ stacks.

Note this article is a 100% copy of Peter Lawrey article :

Java Code Details

How the code works:
In this log, two copies of the class are loaded first. The references to the first class/classloader are overwritten 
by references to the second class/classloader. The first one is cleaned up on a GC, the second one is retained. 
On the second loop, two more copies are initialised. The forth one is retained, the second and third are cleaned 
up on a GC. Finally the forth copy of the static fields are cleaned up on a GC when they are no longer references. 

Java Code LoadAndUnloadMain.java

Copyright (c) 2011.  Peter Lawrey
 *
 * "THE BEER-WARE LICENSE" (Revision 128)
 * As long as you retain this notice you can do whatever you want with this stuff.
 * If we meet some day, and you think this stuff is worth it, you can buy me a beer in return
 * There is no warranty.
 */

package test;

import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;

public class LoadAndUnloadMain {
  public static void main(String... args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InterruptedException {
    URL url = LoadAndUnloadMain.class.getProtectionDomain().getCodeSource().getLocation();
    final String className = LoadAndUnloadMain.class.getPackage().getName() + ".UtilityClass";
    {
      ClassLoader cl;
      Class clazz;

      for (int i = 0; i < 2; i++) {
        cl = new CustomClassLoader(url);
        clazz = cl.loadClass(className);
        loadClass(clazz);

        cl = new CustomClassLoader(url);
        clazz = cl.loadClass(className);
        loadClass(clazz);
        triggerGC();
      }
    }
    triggerGC();
  }

  private static void triggerGC() throws InterruptedException {
    System.out.println("\n-- Starting GC");
    System.gc();
    Thread.sleep(100);
    System.out.println("-- End of GC\n");
  }

  private static void loadClass(Class clazz) throws NoSuchFieldException, IllegalAccessException {
    final Field id = clazz.getDeclaredField("ID");
    id.setAccessible(true);
    id.get(null);
  }

  private static class CustomClassLoader extends URLClassLoader {
    public CustomClassLoader(URL url) {
      super(new URL[]{url}, null);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
      try {
        return super.loadClass(name, resolve);
      } catch (ClassNotFoundException e) {
        return Class.forName(name, resolve, LoadAndUnloadMain.class.getClassLoader());
      }
    }

  }
}

class UtilityClass {
  static final String ID = Integer.toHexString(System.identityHashCode(UtilityClass.class));
  private static final Object FINAL = new Object() {
    @Override
    protected void finalize() throws Throwable {
      super.finalize();
      System.out.println(ID + " Finalized.");
    }
  };

  static {
    System.out.println(ID + " Initialising");
  }
}

Compile and run the code

[oracle@wls1 CLASSLoader_and_GC]$ javac test/LoadAndUnloadMain.java
[oracle@wls1 CLASSLoader_and_GC]$ java test.LoadAndUnloadMain
5552bb15 Initialising
456d3d51 Initialising

-- Starting GC
5552bb15 Finalized.                
-- End of GC

302b2c81 Initialising
2b1be57f Initialising

-- Starting GC
302b2c81 Finalized.
456d3d51 Finalized.
-- End of GC

-- Starting GC
2b1be57f Finalized.
-- End of GC

Reference

Leave a Reply

Your email address will not be published. Required fields are marked *