Search code examples
javaclassloaderinstrumentationjavaagents

Does a class loaded once during premain load again once the rest of the application run


i am trying to instrument some of the classes of a server before it finish start, because i need to monitor some activities of it. In that case, I am using a javaagent with a premain method. This agent loads any required classes and instruments them using retransformClasses of the Instrumentation interface.

I am 100% sure that the class that I am trying to instrument runs within the server because i have instrumented same classes earlier by iterating through all the classes that are loaded by the server. Since that adds overhead at server startup i have been trying to use the above method instead. It worked totally fine when I run outside of the application server. But when I try to run it on the server, the instrumentation part is executed without any exceptions or instrumenting the required classes and methods. But when the application starts on the server, I do not get any outputs from the instrumented methods.

(For example, suppose i am trying to instrument executeQuery method of org.h2.jdbc.JdbcPreparedStatement class. I had to add the h2 jar to the class path)

The code segment below describes the premain method which is used to load each class using the retransformClass method:

for (InstrumentationClass instrumentationClass : instrumentationClasses){
  Class currentClass = ClassLoader.getSystemClassLoader()
                                  .loadClass(instrumentationClass.getClassName());
  SchemaClass currentClassWithDetails = new SchemaClass(
      scenario.getScenarioName(), currentClass, instrumentationClass);
  InstrumentationClassTransformer.transformMe.add(currentClassWithDetails);
  instrumentation.retransformClasses(currentClass);
  InstrumentationClassTransformer.transformMe.remove(currentClassWithDetails);
}

Is it possible that it loads different copies of the same class when the rest of the server starts up? Is there a way to overcome this?


Solution

  • Application servers typically use child-first class loaders to isolate an application from the application server's code. This way, an application server's class loader can simply be removed such that an application is undeployed without restarting the JVM.

    A Java agent access classes via the system ClassLoader which keeps reference to the classes of the server application. Therefore, org.h2.jdbc.JdbcPreparedStatement might exist twice for the different class loaders - the system and the application class loader - where you only instrument the one that is not used in your deployed application.

    I do however wonder how your approach makes sense. Why can't you just install a ClassFileTransformer and check for the name to be org/h2/jdbc/JdbcPreparedStatement and instrument once a class is loaded. The overhead should not be too big. This way, you would instrument the class for any class loader and you woud also reinstrument classes on a redeploy.