Using Class.forName() in Java Instrumentation Agent

java instrumentation example
java attach agent to running jvm
java agent premain
java instrumentation spring boot
java agent api
javaagent classpath
java instrumentation maven
java agent intercept method call

What I understand is that if I use:

Instrumentation#getAllLoadedClasses()

I do get a selection of all loaded classes by the target JVM. But If I do:

Class.forName("my.class.name")

This will not be the same class as the class loaded by VM. Yes, I can add this particular class as a jar in the agent MANIFEST.MF Class-Path - but that does not look the same to me as getAllLoadedClasses().

Could someone please confirm whether this is correct i.e. I would not be able to find a specific class using Class.forName() when instrumenting? My objective was not to iterate over all loaded classes using getAllLoadedClasses() - But if there is no alternative, I guess that's okay for now.

** UPDATE

What I made a mistake in writing is the Boot-Class-Path which I have now corrected in my manifest. Using -verbose:class logging I managed to see that my jars are being loaded as

[Opened C:\fullpath\someother.jar]
[Opened C:\fullpath\another.jar]
[Opened C:\fullpath\different.jar]

But I don't see any corresponding loading information. I tried adding a Class.forName("a.package.in.someother.jar.classname") and got NoClassDefFoundError. As soon as I jump into the agent jar, I cannot use Class.forName() to check if the class is loaded by the target VM. I am getting a NoClassDefFoundError.

FURTHER UPDATE

Okay I have "Fattened" the manifest to look up all classes in my WEB-INF/lib and tomcat's lib directory. What I can see is below:

1) When my custom class MyClass is loaded for the first time. -verbose shows:

[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]

2) If I try to load the class again, it is correctly showing the above order.

3) My agent jar is manifested with all classes for my tomcat lib and my web-inf/lib directory. And I can also confirm that the loader sees the jars correctly.

4) Now I inject the agent, and call Class.forName("my.pkg.MyClass") from within the agent class. I get the below results.

[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]

I acknowledge that it's system class loader loding it inside my agent code as @RafaelWinterhalter pointed out in one of his answers. Is there any way I can force a "Delegation" so that the a different classloader loads the agent class and therefore, correctly redefines a class.

Any help is appreciated.

As it is stated in the javadoc:

Invoking this method is equivalent to: Class.forName(className, true, currentLoader) where currentLoader denotes the defining class loader of the current class.

You can also see from the source code that the method is marked @CallerSensitive which means that you get a different result based on the class loader that invokes the method.

When calling Instrumentation::getAllLoadedClasses, the returned array contains classes of any class loader and not only of the current class loader which is the system class loader when running a Java agent. Therefore:

for (Class<?> type : instrumentation.getAllLoadedClasses()) {
  assert type == Class.forName(type.getName());
}

is not generally true.

Using Class.forName() in Java Instrumentation Agent, What I understand is that if I use: Instrumentation#getAllLoadedClasses(). I do get a selection of all loaded classes by the target JVM. But If I do: Class. The forName () method of Java Class class returns the Class object associated with the class or interface with the given name in the parameter as String.

After a bit of run around, and Thanks to @Holger who reminded me what the problem was - incorrect class loader.

before I inject the agent, I have done the following:

// Get the current context class loader, which is app ext. classLoader
ClassLoader original = Thread.currentThread().getContextClassLoader().getSystemClassLoader();

// Set the system classloader to app classloader which won't delegate anything
Field scl = ClassLoader.class.getDeclaredFields();
scl.setAccessible(true);
scl.set(null, Thread.currentThread().getContextClassLoader());

// Now inject agent
try {
    vm.loadAgent(agentPath, args);
} catch (all sorts of errors/exceptions in chain) {
// Log them and throw them back up the stack.
} finally {
  vm.detach();
  // Put back the classLoader linkage
  sc.set(null, original);
}

How I have confirmed

  1. When it goes in my Agent Class - Thread.currentThread().getContextClassLoader() becomes my application extn loader. But the system classloader now becomes `ParallelWebappClassLoader".

  2. I am assuming this is how it works, but could be totally worng:

    i) When I say Class.forName("my.pkg") it will check the system class loader which is pointing to my loader now. If the class is not found (i.e. not loaded), it will go to parents etc. I believe this is more or less the delegation model is.

    ii) In this way, the class is loaded in VM by the same class loader which would also load the class in my webapp under normal circumstances.

    iii) I will not instrument anything else apart from my own classes so the classloader will always be the same.

So far I have not seen any LinkageError happening. But I still feel this is too risky and if I break the link I am screwed.

Guide to Java Instrumentation, We'll also talk about java agents and how we use them to instrument our code. For simplicity, we'll put this class in the application jar file. new MyAtmApplication().run(args);. } see if we can get the class using forName. Description. The java.lang.Class.forName (String name, boolean initialize, ClassLoader loader) method returns the Class object associated with the class or interface with the given string name, using the given class loader. The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader.

Using Class.forName in a java profiler must be avoided to escape from the NoClassDef error. JVM Loads the class files in the different level of class loaders based on their classpath setting and the class file demand.

Java Cre libraries + Boot path defended libraries will be loaded in bootstrap Level Java Agent will be loaded in system level and it goes on. Class.forName() will look the class files from the parent loaders, the current loader will not check the child loader (Until unless we implement our own loaders)

Java core classes will be accessible from your application code but our application code will not be accessible by the Java core classes. Its called class loader hierarchy.

You have three options.

  1. Lookup the class files from the Instrumentation.GetLoadedClassFiles()
  2. Through Transformers you can get all the loaders classes and you can track them and look for your class in every loader until you find.
  3. Have the Class.forname implementation in the lowest level of the hierarchy so that it can internally access all the path.

Maintain the hierarchy properly to avoid too many weird errors.

Java Examples for java.lang.instrument.Instrumentation, getNodeTextValue(packageNode); for (String s : ps.split(",")) { if (s.trim().isEmpty​()) continue try { //Class.forName("org.helios.apmrouter.codahale.metrics.​Metrics" Project: GroovyMX-master File: AgentInstrumentation.java View source code The given class must not have been loaded before by * the agent. * * @param  In this article we will learn the uses of Class.forName in Java and how it is used in creating objects dynamically. In general Class.forName is used to load the class dynamically where we doesn’t know the class name before hand. Once the class is loaded we will use newInstance() method to create the object

[JDK-8188811] Java Instrumentation Agent's Boot-Class-Path is not , Create a simply instrumentation agent using a class file. forName() and provide a package name of a class that has already been loaded by  The java.lang.Class.forName(String className) method returns the Class object associated with the class or interface with the given string name. Declaration. Following is the declaration for java.lang.Class.forName() method. public static Class<?> forName(String className) throws ClassNotFoundException Parameters

Your first javaagent, You can even use multiple agents at once now and they will be JaCoCo is enabled by bytecode instrumentation - and generally using its javaagent even if in pid = ProcessHandle.current().pid(); // java 9, if you run on java 8 use out the forName in previous code - which can lead to loading classes  To be able to use the Java agent, we must first load it. We have two types of load: static – makes use of the premain to load the agent using -javaagent option; dynamic – makes use of the agentmain to load the agent into the JVM using the Java Attach API; Next, we'll take a look at each type of load and explain how it works. 4.1. Static Load

Class.forName : Java Glossary, Nested Classes. Inner classes, nested static classes and even anonymous classes all have names which you can determine with Class. getName(). You will​  Best Java code snippets using java.lang.instrument. { // Append the agent JAR to the bootstrap search path so that (Class.forName("java.lang.invoke

Comments
  • what makes you say that Class.forName("[...]") finds a different class than Instrumentation.getAllLoadedClasses() ? Is your target class not referenced from the classes that are being loaded?
  • @diginoise I am saying that they are not found at all ! If I load a class "my.package.MyClass" before injecting the agent - the same class is not visible via Class.forName() inside the agent even if I have added relevant jar to Boot-Class-Path I might be wrong, but there is a fundamental gap in the instrumentation info provided on JDK page. I have raised a bug in the system for them to check.
  • The fact that instrumentation can observe (and modify) classes as they are being loaded does not mean that its classloader is responsible for loading these classes. Is your application a Web App ?
  • @dignoise yes it's a webapp, but I believe this will happen even if I had a single helloworld type application. I think you might have snatched the word just out of my mind. The solution would be to force Instrumentation to use VM's context classLoader?
  • you could create custom ClassLoader and pass it your CLASSPATH
  • Thanks - so how could the instrumentation agent loading can be delegated with e.g. ParallelWebappClassLoader which correctly loads all the clasess in my web application? My agent is already in the web application lib directory. Do I need to explicitly load this agent during the webapp startup? That kind of defeats the purpose of "Starting agent after the VM has started".