Examples of code for this article are as follows, see bTrace and arthas for more information:
https://github.com/sayhiai/example-javaagent
Copy the code
After version 5, the JDK has a package called Instrument that does some really cool stuff. Some APM tools on the market are enhanced through it.
This is a necessary skill for infrastructure, but not for business development. Many interviews will ask you about this topic, not because it will be used in the future, but because you are familiar with the JDK and he is trying to take you down a level.
No problem, but you’re going too far to say you don’t know.
Javaagent introduction
Our usual Java entry method is a main method, and the JavaAgent entry method is called premain, indicating that some operation is performed before main runs. Javaagent is a JAR package that defines a standard premain() method and does not need to inherit or implement any other classes.
It’s an agreement, not for any other reason. This method is executed both the first time it is loaded and every time a new ClassLoader is loaded.
We can make some changes to the bytecode in this preloaded method to add functionality or change the behavior of the code. This method is non-invasive and simply requires the -JavaAgent parameter to be added to the startup command. After Java6, it is even possible to load proxy classes dynamically for running application Settings by attach.
Experienced students are bound to object. Instrument has two main methods, preMain and AgentMain. Within a JVM, only one of them will be called. The former is the modification before Main executes, while the latter controls the behavior of the class at runtime. There are some differences, agentMain is a little bit more restrictive because it’s dangerous.
What’s the use of
Getting statistics
Many APM products, such as Pinpoint, SkyWalking, etc. are code enhancements using JavaAgent. Monitoring information is collected by dynamically adding statistical codes before and after method execution. Compatible with OpenTracing, the function of distributed link tracing can be realized. It works like AOP and ultimately exists as bytecode, with performance penalties depending on your code logic.
Hot deployment
With a custom ClassLoader, hot replacement of code can be implemented. Hot deployment is much easier to implement with AgentMain. Once the Instrumentation is retrieved from AgentMain, the class can be dynamically redefined.
diagnosis
Coupled with JVMTI technology, it is possible to attach to a process for runtime statistics and debugging, underlying the popular BTrace and Arthas technologies.
How to do
It can be roughly divided into the following steps:
- Build agent JAR package, write enhanced code
- Specify the premain-class/agent-class attribute in the manifest
- Use parameter loading or attach mode
Write the Agent
The final embodiment of JavaAgent is a JAR package. Create a default Maven project using IDEA.
Create a normal Java class and add a PreMain or AgentMain method with exactly the same parameters.
Write the Transformer
In this section, leverage the capabilities of additional JAR packages.
The actual code logic needs to implement the ClassFileTransformer interface. Suppose we want to count the execution time of a method. We use JavaAssist to enhance bytecode, which can be done with the following code.
- To obtain
MainRun
Class bytecode instance - To obtain
hello
Method - Before and after the method, add time statistics, first define variables
_begin
“And write the code directly
Don’t forget to add maven dependencies
< the dependency > < groupId > org. Javassist < / groupId > < artifactId > javassist < / artifactId > < version > 3.24.1 - GA < / version > </dependency>Copy the code
Bytecode enhancement can also be done using Cglib, ASM, and other tools.
The MANIFEST. Manifest.mf
So how is the code we write known to the outside world? That’s the manifest.mf file. The path is SRC /main/resources/ meta-INF/manifest.mf
The Manifest - Version: 1.0 premain - class: com. Sayhiai. Example. Javaagent. AgentAppCopy the code
Normally, maven packaging overwrites this file, so we need to specify which one is required.
<build><plugins><plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration></plugin></plugins></build>
Copy the code
Then, from the command line, execute MVN Install to the local code base or publish to the private server using MVN deploy.
The manifest.mf parameter list is attached: Premain-Class Agent-Class Boot-Class-Path Can-Redefine-Classes Can-Retransform-Classes Can-Set-Native-Method-Prefix
use
It depends on whether you use PreMain or AgentMain.
premain
Simply add parameters to the startup command line to enable the agent when the JVM starts.
java -javaagent:agent.jar MainRun
Copy the code
In idea, you can attach parameters to JVM options.
Let’s look at the test code.
This is our implementation class. After the command is executed, the output is Hello World. The enhancements also output additional execution times, as well as some debug information. Debug information is output before the main method is executed.
agentmain
It’s used in diagnostic tools. Using the JDK /lib/tools.jar functionality, you can dynamically add functionality to a running program. The main steps are as follows:
- Gets the process ids of all JVMS running on the machine
- Select the JVM to diagnose
- Attach the JVM using the attach function
- Use the loadAgent function to load the agent and dynamically modify the bytecode
- Uninstall the JVM
All of this code is dangerous, which is why Btrace, after all these years, is only used with caution on a small scale. Arthas is relatively friendly and safe.
Pay attention to the point
Jar package dependency mode
Typically, agent jars are provided as fatJars, where all dependencies are packaged into one large JAR.
If you have complex functions and many dependencies, the JAR package will be particularly large.
Maintaining these dependencies using separate BOM files is another approach. Users manage dependency issues themselves, but this usually leads to errors where jar packages cannot be found. Worse, most are discovered at run time.
Duplicate class name
Don’t use the same class names (including package names) as in the JDK and instrument packages. Sometimes you can get away with it, but you can also get caught in exceptions that you can’t control.
Three, do limited functions
As you can see, adding functionality to the system dynamically is cool, but in most cases very performance-intensive. You’ll find that it’s not uncommon for some simple diagnostic tool to take up 1 core of your CPU.
Four, this
If you are using an older JVM and frequently generate a large number of proxy classes, this will cause the PERm section to swell, which will result in OOM.
Classloaders have parent delegates, so if you want to replace a class, be sure to figure out which one its ClassLoader should use. Otherwise, the replacement class will not take effect.
End
Add your enhanced code to zK-like active notifications to dynamically adjust the behavior of your application by managing the background. If you integrate a scripting language like Groovy, in theory, you can do just about anything.
Therefore, using the jar package introduced by -JavaAgent parameter, or using some diagnostic tools provided by ATTACH, little sister dare not use casually.