Do you really get class loaders?Jevgeni KabanovFounder & CTO of ZeroTurnaround
My BackgroundCreator and core developer of JRebelJRebelmaps your project workspace directly to a running application, watches the changes you make to classes and resources, then intelligently reflects them in your application.With JRebel you can get an extra dev month a year by skipping builds & redeploys30 day trial, just $149 per developer :)
To create JRebel we
Hooked into class loading on the JVM levelIntegrated with the class loading mechanism in more than 10 different serversSolved hundreds of issues connected to class loadingLearned a lot more about class loaders than we wanted to 
OverviewBasicsWhat is class loading?How was it meant to work?Problems and solutionsHow do class loaders leak?OSGi, Spring dm, JBoss and othersConclusions
Basics
Class loader APIpublicabstractclassClassLoader {  public Class loadClass(String name);  protected Class defineClass(byte[] b);  public URL getResource(String name);  public Enumeration getResources(String name);  publicClassLoadergetParent()}
Class loadingpublicclass A {publicvoiddoSmth() {    B b = new B();b.doSmthElse();  }}Causes a call toA.class.getClassLoader().loadClass(“B”);
DelegationClass loaders have a parent class loaderThe parent is usually consulted firstAvoids loading same class several timesHowever in a Java EE web module local classes are searched firstIn Java EE each WAR module of an EAR gets its own class loaderThis allows separate namespaces for applications in same container
Java EE Delegation
Problems and solutions
No class foundVariantsClassNotFoundExceptionClassNoDefFoundExceptionHelpfulIDE class lookup (Ctrl+Shift+T in Eclipse)find *.jar -exec jar -tf '{}' \; | grepMyClassURLClassLoader.getUrls()Container specific logs
Wrong class foundVariantsIncompatibleClassChangeErrorAbstractMethodErrorNoSuch(Method|Field)FoundErrorClassCastException, IllegalAccessErrorHelpful-verbose:classClassLoader.getResource()javap -private MyClass
More than one class foundVariantsLinkageError (class loading constraints violated)ClassCastException, IllegalAccessErrorHelpful-verbose:classClassLoader.getResource()
More than one class foundShared ClassLoaderClassCastExceptionUtil3Factory3Factory3.instanceUntyped();new Util3()Util3Test3Web ClassLoaderUtil3 u = (Util3) Factory3.instanceUntyped();
More than one class foundShared ClassLoaderLinkageErrorUtil3Factory3Factory3.instance();new Util3()Util3Test4Web ClassLoaderFactory3.instance().sayHello();
More than one class foundShared ClassLoaderIllegalAccessErrorUtil3Factory3Factory3.instancePackage();new Util3()Util3Test5Web ClassLoaderUtil3 u = (Util3) Factory3.instancePackage();
Reloading an ObjectOldClassLoaderNewClassLoaderMyObject.classMyObject.classRecreate the objectMyObjectMyObject
Leaking ClassLoadersClassLoaderClass1.classClass2.classClass3.classStatic FieldsStatic FieldsStatic Fields
Leaking ClassLoadersExample.classExample.classExample.classExampleFactory$1ExampleFactory$1ExampleFactory$1Leak.classLeak.classLeak.classLeakLeakLeak
State of the art
Hierarchy is not enough?Isolation Different versions of the same libraryPerformanceClass lookup is very slowRestrictedWhy siblings can’t see each other’s classes?OSGi, JBoss, NetBeans and others implement a different system
The Modern WayEach JAR has own class loaderAll class loaders are siblings, with one central repositoryEach JAR explicitly declaresPackages it exportsPackages it importsRepository can find relevant class loaders by package
Modern FilteringclassMClassLoaderextendsClassLoader {// Initialized during startup from imports  Set<String> imps;public Class loadClass(Stringname) {    String pkg = name.substring(0, name.lastIndexOf('.'));if (!imps.contains(pkg))returnnull;returnrepository.loadClass(name);  }}
Modern LookupclassMRepository {  // Initialized during startup from exports  Map<String,List<MClassLoader>> exps;  publicClass loadClass(String name) {    String pkg = name.substring(0,    name.lastIndexOf('.'));    for (MClassLoadercl : exps.get(pkg)) {      Class result = cl.loadLocalClass(name);      if (result != null) return result;    }    returnnull;  }}
TroubleshootingThe same tricks also work with Modern class loading systemsClassLoader.getResource();-verbose:classOften can be supplemented with custom toolsNeed to think in terms of export/import in addition to classpathLooking at the pseudocode can help
ProblemsToo restrictiveImport is a one-way streetIf you want to use Hibernate, you import it, but it cannot access your classesEasy to leakAny references between class loaders are leaks waiting to happen DeadlocksJVM enforces a global lock on loadClass()
ConclusionsThe trick of troubleshooting class loaders is understanding how they work :)Modern systems add a level of complexity on top of an abstraction that nobody gets to begin withWhen redeploying or reloading classes leaking is easy and leads to OOMWe need better tools to troubleshoot class loaders!

Do you really get class loaders?

Editor's Notes

  • #7 Class loaders are Java objectsClass loaders are responsible forFinding resourcesResolving class names into Class objectsOriginally motivated by applets
  • #8 Every class has a reference to its class loader objectMyClass.class.getClassLoader()