1

I wonder which classes I can not intercept and manipulate by using byte code transformation and java agents.

Q1: I know not all classes can be redefined (altered, manipulated) on load as well as later on. These classes include methods that are non-native but replaced by hard-wired native implementations (like some Spring and System methods). I wonder what classes are off limits for byte code manipulation for one or both of on-load and/or redefine after load?

Q2: Did this set of types and methods not able to be altered has changed for recent JDK / JRE versions?

Q3: If I manipulate the JVM by altering the default class loader (no fancy changes thou), would I be able to increase the number of types possible to be redefined and what and why?

[Additional]

I did quite some research and even ran an agent myself and see what comes along. Basically there are quite some classes missing. Loading all of the JRE classes in the class path one sees some missing classes loaded before the agent mechanism kicks in. It is of cause normally since even the agent needs classes to run and those classes need classes... . But I wonder what is the set of classes one can never alter, why is that so as well as can hacking the JVM bring you any further.

I try to wade into the JVM / Java manipulation basically in-order to understand everything as well as add some nice monitoring tools to my tool-belt. Also I am implementing a class reloading solution for my bigger project.

Martin Kersten
  • 5,127
  • 8
  • 46
  • 77
  • " Please, try to transform it to English." - Apparently coming from Berlin you might want to rephrase this unnecessary offending statement. – Martin Kersten Oct 05 '15 at 13:39
  • I just wanted to inform you that your statement was unnecessarily offensive. Why not just saying, redefine is unclear to me, please add more information. And yes I did a research actually but never found information about this one. – Martin Kersten Oct 05 '15 at 21:12

1 Answers1

0

Well, I did a simple test using

public static void premain(String agentArgs, Instrumentation inst) {
  System.out.println(System.getProperty("java.version"));
  System.out.println(inst.isRedefineClassesSupported());
  int num=0;
  for(Class<?> clazz:inst.getAllLoadedClasses())
    if(!clazz.isArray() && !inst.isModifiableClass(clazz)) {
      System.out.println("not modifiable "+clazz);
      num++;
    }
  System.out.println((num==0? "all classes are": num+" classes are not")+" transformable");
}

and as of 1.7.0_51 and 1.8.0_60 it printed:

true
all classes are transformable

In other words, your first assumption is wrong. There is no restriction about which classes can be redefined later-on. Of course, when you redefine essential classes, you have to take a lot of care to avoid screwing everything up.

You are right when you assume that upon java agent start, there are already loaded classes for which load-time transformation is not possible. Note that you can use the same method used in the code above, getAllLoadedClasses(), to learn which classes are already loaded. In my setup, it returns more than 400 non-array classes.

The only safe assumption you can make about them, is, that the exact set is deliberately unspecified as otherwise, no changes to the implementation of the JVM bootstrap process were possible. That would be a tough restriction—imposed by a non-standard feature…


Manipulating the system class loader does not change anything, as these classes are not loaded by the system class loader, but by the bootstrap loader. This is the class loader which is represented as null because it can not represented by a Java object instance.

You can verify it via:

public static void premain(String agentArgs, Instrumentation inst) {
  System.out.println(System.getProperty("java.version"));
  System.out.println(inst.isRedefineClassesSupported());
  int num=0;
  for(Class<?> clazz:inst.getAllLoadedClasses())
    if(clazz.getClassLoader()!=null) {
      System.out.println("already loaded "+clazz);
      num++;
    }
  System.out.println(num+" non-bootstrap class(es) loaded");
}

In my setup, it printed:

1.8.0_60
true
already loaded class ExperimentalAgent
1 non-bootstrap class(es) loaded

showing that the only class loaded through the system class loader is the agent itself, all other already existing classes are loaded and defined by the bootstrap loader.

When classes loaded by the system class loader contain references to core classes, they are resolved by asking the system class loader, however, it has no other option than delegating to its parent loader to get them resolved in a compatible way. Otherwise, the redefined classes were considered to be different classes than those resolved within the bootstrap loader (i.e. when the core classes reference each other). Note that for classes whose qualified name begins with java., attempts to define them on a Java-side class loader are rejected anyway:

ClassLoader.defineClass:

Throws:

SecurityException - If an attempt is made to add this class to a package that contains classes that were signed by a different set of certificates than this class (which is unsigned), or if name begins with "java.".

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thanks Holger, these are excellent thoughts. If only one class gets bootstrapped (did not have thought it is that less), I might go this route: I use a very very small Java Agent to load a 'so' library starting a JVM in the child process containing the actual agent. Therefore the complete agent runs isolated from the VM under observation. Nice. Really forgot about that option. So thanks again for you thoughts. You get the green check mark. I will start screwing some things over and see how it goes from here. – Martin Kersten Oct 06 '15 at 11:21
  • No, the one class is the the agent class. As already written, it’s roughly 400 core classes that are loaded by the time. Still, that’s a rather small footprint and lightweight agents are possible. Nevertheless, if the only thing it does, is loading an `so` library, you don’t need a JVM in the parent process at all… – Holger Oct 06 '15 at 11:55
  • I will investigate it further but actually this version would follow the Java path, can be initiated by the meta data and is therefore painless to deploy. But you are right I will check out the pure C solution this afternoon. The final goal is to reduce the (class/memory/object) footprint in the javaVM under observation. – Martin Kersten Oct 06 '15 at 12:41