ClassLoader
when an application is loaded and running. A number of folks posted various solutions like using java -verbose
which are of limited help. Another solution was to get using something like ClassLoader.getSystemClassLoader();
. The latter looks very promising, but is wrong. I knew that there are a number of classes that are loaded that this would not display.Ssssh... I will show you how I know.
The problem and solution is surprisingly non-trivial. I thought I would come up with a solution like the one above in 5 minutes. I did come up with one above in about that much time. It turns out to be incorrect.
The solution is to use a Java agent to instrument the JVM and see what it is loading. I am sure a number of you have seen the
-javaagent:[=]
flag for the VM and wondered what is that for. I am going to show you.First some results:
all length -> 815
system length -> 163
appLoader length -> 163
classes size -> 61
The first value all indicates all of the classes loaded by the JVM. That is a lot of classes. This is via an Instrumentation agent.
The second value system indicates all of the classes loaded by the System
ClassLoader
. This is significantly less than loaded by the JVM. This is via an Instrumentation agent.The third value is the appLoader which is the application classloader. It matches the System, but this may not always be the case. This is via an Instrumentation agent.
Finally, the last value classes is what you get from the
ClassLoader
without instrumentation. It is a paltry amount of the total classes loaded.
So which one is right? Good question... Here is an answer only a parent, or teacher can give. "It depends."
If I am looking at everything being loaded to check for something forensically I would use the 815 and look at what these classes are and where they came from. If I am checking which classes are loaded to help with reflection, I would look at the 61.
If you have read this far, then you want the code to look at. I have split it into a couple of NetBeans Maven projects hosted on BitBucket using Mercurial.
Code
InstrumentationAgent.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | package com.bluelotussoftware.instrumentation.agent; import java.lang.instrument.Instrumentation; import java.util.logging.Level; import java.util.logging.Logger; /** * This provides a * <code>javaagent</code> instrumentation for the VM. This class provides * convenience methods for getting the classes loaded by the VM. * * @author John Yeary * @version 1.0 */ public class InstrumentationAgent { private static final Logger log = Logger.getLogger(InstrumentationAgent. class .getName()); private static Instrumentation instrumentation; /** * This method loads the agent prior to invoking the main method. * * @param agentArgs Arguments to be passed to the agent. * @param inst Instrumentation (agent). * @throws Exception if any {@code Exception} occurs while calling the * method and creating the agent. */ public static void premain(String agentArgs, Instrumentation inst) throws Exception { log.log(Level.INFO, "premain() method called with agentArgs {0} and inst {1}" , new Object[]{agentArgs, inst.getClass()}); InstrumentationAgent.instrumentation = inst; } /** * This method is used to set the agent on the main method after the JVM is * already running. * * @param agentArgs Arguments to be passed to the agent. * @param inst Instrumentation (agent). */ public static void agentmain(String agentArgs, Instrumentation inst) { log.log(Level.INFO, "agentmain() method called with agentArgs {0} and inst {1}" , new Object[]{agentArgs, inst.getClass()}); InstrumentationAgent.instrumentation = inst; } /** * This method returns the wrapped instrument (agent). * * @return The wrapped agent. */ public static Instrumentation getInstrumentation() { return instrumentation; } /** * Returns an array of all classes currently loaded by the JVM. * * @return an array containing all the classes loaded by the JVM, * zero-length if there are none. */ public static Class[] getAllLoadedClasses() { return instrumentation.getAllLoadedClasses(); } /** * Returns an array of all classes for which * {@link ClassLoader#getSystemClassLoader()} is an initiating loader. * * @return an array containing all the classes for which * {@link ClassLoader#getSystemClassLoader()} is an initiating loader, * zero-length if there are none. */ public static Class[] getSystemClassLoaderInitiatedClasses() { return instrumentation.getInitiatedClasses(ClassLoader.getSystemClassLoader()); } /** * Returns an array of all classes for which loader is an initiating loader. * If the supplied loader is null, classes initiated by the bootstrap class * loader are returned. * * @param classLoader the loader whose initiated class list will be * returned. * @return an array containing all the classes for which loader is an * initiating loader, zero-length if there are none */ public static Class[] getClassLoaderInitiatedClasses( final ClassLoader classLoader) { return instrumentation.getInitiatedClasses(classLoader); } /** * Static initialization method to load the {@link Instrumentation} if it * has not already been loaded. */ public static void initialize() { log.log(Level.INFO, "initialize() method called." ); if (instrumentation == null ) { log.log(Level.INFO, "Instrumentation was null calling AgentLoader." ); AgentLoader.loadAgent(); } } } |
AgentLoader.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package com.bluelotussoftware.instrumentation.agent; import com.sun.tools.attach.AgentInitializationException; import com.sun.tools.attach.AgentLoadException; import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; import java.io.IOException; import java.lang.management.ManagementFactory; /** * * This class is responsible for dynamically loading the instrumentation agent * jar. * * @author John Yeary * @version 1.0 */ public class AgentLoader { /** * This method returns the path to the {@link Instrumentation} agent located * in the .m2 repository. * * @return Path to the agent jar. */ private static String getAgentPath() { return new StringBuilder().append(System.getProperty( "user.home" )) .append( "/.m2/repository/com/bluelotussoftware/instrumentation-agent/1.0/instrumentation-agent-1.0.jar" ) .toString(); } /** * Static method for loading an agent into the currently running JVM. */ public static void loadAgent() { String runtimeMXBeanName = ManagementFactory.getRuntimeMXBean().getName(); int endIndex = runtimeMXBeanName.indexOf( '@' ); String pid = runtimeMXBeanName.substring( 0 , endIndex); try { VirtualMachine vm = VirtualMachine.attach(pid); vm.loadAgent(getAgentPath()); vm.detach(); } catch (AttachNotSupportedException | IOException | AgentLoadException | AgentInitializationException e) { throw new RuntimeException(e); } } } |
App.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | package com.bluelotussoftware.example; import com.bluelotussoftware.instrumentation.agent.InstrumentationAgent; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author John Yeary <jyeary@bluelotussoftware.com> * @version 1.0 */ @SuppressWarnings ( "UseOfObsoleteCollectionType" ) public class App { static { InstrumentationAgent.initialize(); } public static void main(String[] args) throws ClassNotFoundException { App app = new App(); Class[] all = InstrumentationAgent.getAllLoadedClasses(); System.out.println( "all length -> " + all.length); Class[] system = InstrumentationAgent.getSystemClassLoaderInitiatedClasses(); System.out.println( "system length -> " + system.length); Class[] appLoader = InstrumentationAgent.getClassLoaderInitiatedClasses(App. class .getClassLoader()); System.out.println( "appLoader length -> " + appLoader.length); List<String> classes = app.getLoadedClasses(ClassLoader.getSystemClassLoader()); System.out.println( "classes size -> " + classes.size()); for (String s : classes) { System.out.println(s); } } public List<String> getLoadedClasses( final ClassLoader classLoader) { List<String> classNames = null ; try { Field f = ClassLoader. class .getDeclaredField( "classes" ); f.setAccessible( true ); List<Class> classes = new ArrayList<>((Vector<Class>) f.get(classLoader)); classNames = new ArrayList<>(classes.size()); for (Class c : classes) { classNames.add(c.getCanonicalName()); } return classNames; } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { Logger.getLogger(App. class .getName()).log(Level.SEVERE, null , ex); } return classNames; } } |
0 comments :
Post a Comment