17

I searched the internet for more than couple of hours and could not reach any conclusion.

Recently I decided to use BouncyCastle for SSL but I wanted it to off by default, so that BouncyCastle jar may not be in the class path.

private void enableBouncyCastleForSSL() {
   if (config.isBouncyCastleEnabled()) {
        Security.insertProviderAt(new BouncyCastleProvider(), 1);
    }
} 

Even when config is disabled, it was looking for BouncyCastle and it failed with class loader error. java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider

I tried moving just the line Security.insertProviderAt(new BouncyCastleProvider(), 1); to a new method it exhibited the same problem.

But when I introduce a class and move the BouncyCastle inside it, when the config is disabled, class loader issue does not appear

private void setupSSLProvider() {
    if (voldemortConfig.isBouncyCastleEnabled()) {
        SetupSSLProvider.useBouncyCastle();
    }
}
public class SetupSSLProvider {
  public static void useBouncyCastle() {
    Security.insertProviderAt(new BouncyCastleProvider(), 1);
  }
}

Some articles claim that Class is loaded only when it is first used. http://www.programcreek.com/2013/01/when-and-how-a-java-class-is-loaded-and-initialized/

Apparently in my case, Java8 loads the class referenced in a class.

So my understanding is Java will load the classes one level deep, before executing first line of code in a class. Is that right ?

Arun Thirupathi
  • 351
  • 2
  • 11
  • 1
    Have you considered consulting the Java Language Specification and the Java Virtual Machine Specification? rather than just arbitrary Internet rubbish? Your citation appears to be 14 years old, and relies on empirical investigation, rather than on official specifications. In fact the IBM document cited specifically states "The basic principle is that classes are only loaded when needed (or at least appear to be loaded this way -- the JVM has some flexibility in the actual loading, but must maintain a fixed sequence of class initialization)", which should have been a warning to the author. – user207421 Dec 14 '15 at 03:33
  • Certainly that is an option, but I thought this is some standard that most people should be aware of. There are tons of articles written about the class loading ( 3 types of class loader but not any specifics). Reading Specification seems like a day or two and the language is not written for laymen. If I would frame my question differently, when is a class loaded, there can be 3 answers, Class/Method referencing it is loaded or when it is accessed. I would be surprised if I have to read Specification to get to this answer. – Arun Thirupathi Dec 14 '15 at 06:15

1 Answers1

22

There is no simple answer to this question. The specification leaves room for different implementation strategies and even within one implementation, it will depend on how the class is been used. Further, your question is more about “when can it fail” which depends on why it may fail, e.g. a class may be loaded at one point of time to verify its existence but initialized at a later time when it is actually first‑time used. At both points of time, the operation may fail causing a NoClassDefFoundError.

We may categorize referenced classes into three groups:

  • classes that must be resolved at linkage time (e.g. the superclass)
  • classes that must be loaded at verification time
  • classes whose loading can be deferred to it’s first actual use

In your case, BouncyCastleProvider must be loaded at verification time, rather than its first actual use, because you are passing the result of new BouncyCastleProvider() as argument to the method Security.insertProviderAt(…) and the verifier must check whether the class actually extends the Provider type which the method’s formal parameter mandates.

When verification will happen, is also implementation specific as at least the following possibilities are allowed:

  • eagerly traversing all referenced classes
  • on the loading of the containing class or its first use
  • on the containing method’s first use
  • right before executing the offending instruction

Oracle’s JVM prefers a method’s first use, read: verify it at the method entry, thus, moving the invocation into another method that won’t get executed, be it in another class or not, is sufficient in your case.

But to be compatible with other JVMs, moving it into another class is safer, but to comply to all possible JVMs, you need to even load this other class via Reflection to avoid a direct reference that can be traversed by an eager implementation (or instantiate BouncyCastleProvider reflectively via Class.forName("… .BouncyCastleProvider").newInstance() in the first place).

Holger
  • 285,553
  • 42
  • 434
  • 765
  • it seems I am hitting sort of the same thing :(. Are you saying that if I have a declaration like `interface CustomAction extends Action` and a method `public void go(Action action)` and `bootstrap` is loading `Action.class`, while another classloader is trying to invoke `go` this would fail? In my case I get a `LinkageError` and I *think* I understand now why, but can't really tell if this is solvable at all... – Eugene Feb 01 '19 at 10:47
  • 1
    @Eugene normally, the order of class loading should not matter. Here, there's the special case of an absent class where it can make a difference. Your description doesn't contain such a scenario. – Holger Feb 07 '19 at 11:43
  • agreed. I really hope to have the time to post a proper question about this, but in the end the `LinkageError` was coming from the usage of `MethodHandles` that would create a handle to a certain method (that takes an interface as argument). Weird enough, when I changed to pure reflection (I guess something to do with verifier), the linkage error was gone. I hate that I don't really understand why though :( – Eugene Feb 09 '19 at 03:32