public static void main(String[] args)
compiled them and executed them with a command like java HelloWorld
.java.lang.ClassLoader
are used to directly load classes, as evidenced by the signature of the Class loadClass(String name)
method Class loadClass(String name)
. This method should find the byte array that is the bytecode of the required class and pass it to the protected Class defineClass(String name, byte[] b, int off, int len)
, which will turn it into an instance of java.lang.Class
. Thus, by implementing their loaders, developers can download from any place where they can get an array of bytes;getParent
method of the ClassLoader
class. When you start the JVM, a vertex of this hierarchy of three main loaders is created: the base one (Bootstrap Classloader, responsible for loading the base classes of the framework), the extension loader (Extension Classloader, responsible for loading classes from lib / ext) and the system loader (System Classloader, responsible for loading custom classes). Further, developers are free to continue this hierarchy from the bootloader and below. By default, the HotSpot JVM uses the sun.misc.Launcher$AppClassLoader
class as the system bootloader, but you can easily override it using the java.system.class.loader
system property of the java.system.class.loader
command line key java -Djava.system.class.loader=..
;java.system.class.loader
- from the command line you can do it like this: java -Djava.system.class.loader=.. HelloWolrd
-Djava.system.class.loader
. As it turned out, there is also an elegant solution for this - you need to use a special environment variable, the value of which is automatically added to the launch parameters of any JVM. During the search, two variables were found that could be responsible for this feature: JAVA_OPTS and JAVA_TOOL_OPTIONS. However, none of the articles gave a clear answer to the question: what is the difference between these two variables? It was decided to find the answer to this question empirically. During the experiment, it was found that the truly “magical” is the JAVA_TOOL_OPTIONS variable, the value of which is automatically added to the launch parameters of any HotSpot JVM launched. And JAVA_OPTS is the result of a tacit agreement between developers of various Java applications. This variable is explicitly used by many scripts (for example, startup.sh/startup.bat to start Apache Tomcat), but no one guarantees that all script developers will use this variable.JAVA_TOOL_OPTIONS
environment JAVA_TOOL_OPTIONS
is set, we run the application, work, open the log and see ... a scant list of a dozen classes, including system and several third-party classes. It was here that I had to recall the non-obligation to execute the delegation delegation rule, as well as to look into the source code for Apache Ant and Tomcat. As it turned out, these applications use their own class loaders. This, on the one hand, partly allowed them to acquire their powerful functionality. However, for one reason or another, the developers of these products decided not to adhere to the recommended rule for delegating loading and the loaders written by them do not always turn to their parents before downloading the next class. That is why our system loader knows almost nothing about classes that are loaded by Tomcat and Ant.byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
method byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
the ClassFileTransformer
interface called by the transformer class implementing loading any class. This method turned out to be the bottleneck through which any loadable class passes, with the exception of a very small number of system classes.ClassFileTransformer.transform
method, which, however, will not perform any transformation, but will only write the name of the loaded class to a file. package com.test; import java.io.File; import java.lang.instrument.Instrumentation; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; import java.lang.instrument.IllegalClassFormatException; public class LoggingClassFileTransformer implements ClassFileTransformer { public static void premain(String agentArguments, Instrumentation instrumentation) { instrumentation.addTransformer(new LoggingClassFileTransformer()); } /** * * @param className * @return classfileBuffer - */ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { log(className); return classfileBuffer; } // lib/ext private static final File logFile = new File(System.getProperty("java.ext.dirs").split(File.pathSeparator)[0]+"/LoggingClassFileTransformer.log"); public static synchronized void log(String text) { // // ... } }
public static void premain(String paramString, Instrumentation paramInstrumentation)
. From the name of the method it is clear that it is called before calling the main method. At this moment, you can connect transformer classes to your application using the addTransformer
method of the addTransformer
interface. Thus, the above class is both a transformer class and a premain class. To use this class, it must be placed in a JAR file whose manifest (file META-INF / MANIFEST.MF) contains the Premain-Class parameter indicating the full name of the premain class, in our case Premain-Class: com.test.LoggingClassFileTransformer
. Then you need to specify the full path to this archive using the -javaagent
parameter when starting the JVM. This is where the JAVA_TOOL_OPTIONS variable comes to the rescue.JAVA_TOOL_OPTIONS=-javaagent:" LoggingClassFileTransformer.jar"
set, the application is running, the log is compiled, PROFIT!-verbose:class
parameters -verbose:class
or --XX:+TraceClassLoading
. When using any of these parameters, messages like [Loaded java.util.Date from shared objects file]
added to the standard JVM output stream. However, this method, despite its simplicity, has one major drawback - it is difficult to control the format of the displayed message, as well as the direction of the output. And the application in question already displays enough debugging information to stdout, and the ability to filter out the necessary messages from the stream and redirect them to a separate file for all JVM instances running on the server is very problematic.java -XX:+TraceClassLoading -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=java_*.log -XX:-DisplayVMOutput HelloWorld
<?xml version='1.0' encoding='UTF-8'?> <hotspot_log version='160 1' process='580' time_ms='1334248301214'> <vm_version> <name> Java HotSpot(TM) Client VM </name> <release> 20.6-b01 </release> <info> Java HotSpot(TM) Client VM (20.6-b01) for windows-x86 JRE (1.6.0_31-b05), built on Feb 3 2012 18:44:09 by "java_re" with MS VC++ 7.1 (VS2003) </info> </vm_version> <vm_arguments> <args> -XX:+TraceClassLoading -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=java_*.log -XX:-DisplayVMOutput </args> <command> test </command> <launcher> SUN_STANDARD </launcher> <properties> java.vm.specification.name=Java Virtual Machine Specification java.vm.version=20.6-b01 java.vm.name=Java HotSpot(TM) Client VM java.vm.info=mixed mode, sharing java.ext.dirs=C:\Program Files\Java\jre6\lib\ext;C:\WINDOWS\Sun\Java\lib\ext java.endorsed.dirs=C:\Program Files\Java\jre6\lib\endorsed sun.boot.library.path=C:\Program Files\Java\jre6\bin java.library.path=C:\WINDOWS\system32;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files\PC Connectivity Solution\;C:\Program Files\Rockwell Software\RSCommon;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\Java\jdk1.6.0_06\bin;c:\hibernate;C:\WINDOWS\system32\WindowsPowerShell\v1.0;C:\Program Files\TortoiseSVN\bin;C:\Program Files\Nmap;;C:\PROGRA~1\COMMON~1\MUVEET~1\030625;C:\PROGRA~1\COMMON~1\MUVEET~1\030625;. java.home=C:\Program Files\Java\jre6 java.class.path=. sun.boot.class.path=C:\Program Files\Java\jre6\lib\resources.jar;C:\Program Files\Java\jre6\lib\rt.jar;C:\Program Files\Java\jre6\lib\sunrsasign.jar;C:\Program Files\Java\jre6\lib\jsse.jar;C:\Program Files\Java\jre6\lib\jce.jar;C:\Program Files\Java\jre6\lib\charsets.jar;C:\Program Files\Java\jre6\lib\modules\jdk.boot.jar;C:\Program Files\Java\jre6\classes java.vm.specification.vendor=Sun Microsystems Inc. java.vm.specification.version=1.0 java.vm.vendor=Sun Microsystems Inc. sun.java.command=test sun.java.launcher=SUN_STANDARD </properties> </vm_arguments> <tty> <writer thread='1504'/> [Loaded java.lang.Object from shared objects file] ... <writer thread='3092'/> <destroy_vm stamp='0.281'/> <tty_done stamp='0.283'/> </tty> <hotspot_log_done stamp='0.283'/> </hotspot_log>
-XX:-DisplayVMOutput
option -XX:-DisplayVMOutput
.Source: https://habr.com/ru/post/141699/
All Articles