Prospects for the feed

Java hot update explains how to manually replace class files and then create new Classloaders repeatedly by listening to see if the files have been modified.

Hot updates in this way are supported natively by the JVM, but the disadvantages are obvious:

  1. You need to manually modify files

  2. Creating class loaders repeatedly and making them difficult to uninstall can add to the burden on the system

  3. The code is intrusive and needs to be modified to some extent

What is JavaAgent?

As the name implies, JavaAgent is a tool that can be used as a Java proxy, which is simply a Java section that can be used for writing. The main function of javaAgent is to provide users with the ability to read bytecode files into memory after the JVM. The ability for the JVM to modify the bytecode of a Class object before it is generated in the Java heap using the corresponding byte stream, and thus the JVM will create new Class objects using the modified bytecode (breaking the rule that a Class can only be loaded once).

The use of JavaAgent is non-invasive to your own code.

From a functional point of view, it perfectly solves the shortcomings of our custom class loader to implement hot updateCopy the code

The use of the javaagent

Javaagent is classified into two types according to loading time

  1. From the command line, you declare javaAgent when you start a Java project

  2. For a running project, use JavaAgent, which is like a pluggable tool that is started as needed.

Use javaAgent – Premain mode before JVM startup

Method of use

Javaagent itself, as an argument to a Java command, can specify an additional JAR package that contains the logic you expect to implement through JavaAgent before your own project starts

The command line demo is as follows:

There is no limit to the number of -JavaAgent arguments in a Java program, so you can add as many JavaAgents as you want. All Java Agents will execute in the order you define

java -javaagent:agent1.jar -javaagent:agent2.jar -jar MyProgram.jar
Copy the code
  1. Agent1. jar, agent2.jar is the generated Agent JAR package that you package as required and contains the processing that you expect to do before the class is loaded

  2. Myprogram. jar jar package for your own business

Implementation process of agent JAR package in Premain mode

1. Define the manifest.mf file

You first need a configuration file, which usually contains the following configuration

The Manifest - Version: 1.0 Premain - Class: javaagent. PreMainTraceAgentCopy the code
Parameter names meaning
Manifest-Version The MANIFEST. MF version
Premain-Class The full name of the class containing the premain method

2. Premain method

Create a class that contains a static method called premain, without inheritance, and the main modified logic is contained in the transform method

package javaagent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; public class PreMainTraceAgent { public static void premain(String agentArgs, Instrumentation Inst) {// agentArgs External parameters // instances of Inst Instrumentation System.out.println("agentArgs: "+ agentArgs); Inst.addtransformer (new DefineTransformer(), true); // Add a class converter, similar to registering an interceptor. } // Every time the class is loaded, the transform method in ClassFileTransformer is triggered. Therefore, in this method, before the class is loaded, Static class DefineTransformer implements ClassFileTransformer{@override public byte[] transform(ClassLoader) loader, String className, Class<? > classBeingRedefined, ProtectionDomain protectionDomain, Byte [] classfileBuffer) throws IllegalClassFormatException {/ / conversion method can be implemented in the bytecode changes, specific modifications can use the way such as ASM / / demo simply print string, System.out.println("premain load Class:" + className); return classfileBuffer; }}}Copy the code

3. Pack them into jars

Check that the manifest.mf file is properly packaged using Maven or Gradle

The project structure is as follows

----src
--------main
--------|------java
--------|----------javaagent
--------|------------PreMainTraceAgent
--------|resources
-----------META-INF
--------------MANIFEST.MF
Copy the code

Operation principle of premain mode

1. Create and initialize JPLISAgent

2. Parameters of the manifest.mf file, and set some contents of the JPLISAgent according to these parameters

3. Listen for VMInit events and do the following after JVM initialization is complete:

InstrumentationImpl InstrumentationImpl InstrumentationImpl InstrumentationImpl

(2) Listen to ClassFileLoadHook;

(3) call InstrumentationImpl loadClassAndCallPremain method, in this way will MANIFEST to invoke the javaagent. MF in the specified method of Premain Premain – Class Class

The advantages and disadvantages

advantages

  1. Similar to the section, it can successfully intercept part of the system class and user class, and modify the bytecode before the first loading of the class. It is non-invasive and transparent to the business

  2. The custom transform method in ClassFileTransformer intercepts the trigger every time a classLoader loads a class, which means that if you can get the classLoader to reload the class, this logic will work

disadvantages

  1. Classes in the JVM are only loaded once by the classloader, so normally the transform method is executed only once for a class, the same classloader, and hot updates cannot be implemented without redefining the classloader to load the class.

  2. There is no way to use the javaAgent functionality for Java projects that are already running

JVM startup uses JavaAgent – AgentMain mode

The Java upgrade provides the AgentMain schema on top of the PreMain schema.

In short, AgentMain can load a class after it has been loaded, that is, redefined, and you can change the class at the time of the redefinition without even creating a new classloader. The JVM has already redefined the class internally (the redefinition process is quite complicated).

Agentmain can work directly on a running Java program, so load the running Java program by launching a JAR containing AgentMain using attach

This section describes how to implement the Agent JAR package in AgentMain mode

1. Define the manifest.mf file

You first need a configuration file, which usually contains the following configuration

Manifest-version: 1.0 can-re-re-classes: true can-retransform-classes: true agent-class: cn.think.in.java.clazz.loader.asm.agent.PreMainTraceAgentCopy the code
Parameter names meaning
Manifest-Version The MANIFEST. MF version
Can-Redefine-Classes True indicates that classes required by this agent can be redefined, default is false (optional)
Can-Retransform-Classes True indicates that classes required by this agent can be reconverted, default is false (optional)
Agent-Class The full name of the class containing the AgenMain method

Can re-define -Classes and Can- retransform-classes must be true to allow the binary stream of a class to be redefined (changing the bytecode) after reading. And then the corresponding class loading process.

2. Agentmain method

Create a class that contains a static method called AgentMain, without inheritance, and the main logic for changes is contained in the Transform method

public class AgentMainTraceAgent { public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException { System.out.println("Agent Main called"); System.out.println("agentArgs : " + agentArgs); inst.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<? > classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("agentmain load Class :" + className); return classfileBuffer; } }, true); inst.retransformClasses(Account.class); }Copy the code

By executing the retransformClasses method, the process of reloading a class in a running Java program is intercepted by the loaded AgentMain method to execute the corresponding logic

inst.retransformClasses(Account.class); This code means to convert the target class, which is the Account class. That is, which class you need to redefine needs to be specified, otherwise the JVM cannot know.Copy the code

3. Pack a JAR package

Check that the manifest.mf file is properly packaged using Maven or Gradle

4. Attach using attach

Attach to a running Java process using the Attach (PID) method of the VirtualMachine class. The agent JAR can then be injected into the process using loadAgent(agentJarPath). The corresponding process then calls the AgentMain method.

List<VirtualMachineDescriptor> List = virtualmachine.list (); // attach target VM VirtualMachine. Attach (Descriptor. Id ()); VirtualMachine#loadAgent(" Jar path "," command parameter ");Copy the code

How the AgentMain mode works

1. Create and initialize JPLISAgent

2. Parse the parameters in manifest.mf and set some contents in JPLISAgent according to these parameters

3. Listen for VMInit events and do the following after JVM initialization is complete:

InstrumentationImpl InstrumentationImpl InstrumentationImpl InstrumentationImpl

(2) Listen to ClassFileLoadHook;

(3) call InstrumentationImpl loadClassAndCallAgentmain method, in this way will be to call the javaagent MANIFEST. The designated Agent in the MF – Class Class agentmain method.

The advantages and disadvantages

advantages

  1. The ability to load Java Agent directly to a Java program at run time without specifying it at startup

  2. Reload an already loaded class without redefining the classloader

  3. Non-intrusive and transparent to the business

  4. More perfect implementation of hot update function

disadvantages

  1. Because it involves reloading a class pair, there are certain requirements for class bytecode modification, which are as follows
1. The parent class is the same; 2. The number of interfaces should be the same; 3. Class accessors must be consistent. 4. The field number and field name must be the same. 5. The new method must be private static/final; 6. You can delete the modification method.Copy the code

Premain versus AgentMain

Premain agentmain and two ways of the objective is to eventually callback Instrumentation instance and activate the sun. The instrument. InstrumentationImpl# transform () (InstrumentationImpl is I The implementation class of nstrumentation), which calls back to register ClassFileTransformer in Instrumentation to implement bytecode modifications, is essentially no different in functionality. The differences between the two non-essential functions are as follows:

  1. Premain is introduced in JDK1.5, and agentMain is introduced in JDK1.6. After JDK1.6, you can choose to use preMain or agentMain.

  2. Premain Requires the command line to use the external proxy JAR package, that is, -javaAgent: path of the proxy JAR package. Agentmain can attach directly to the target VM using the Attach mechanism to load the agent. In other words, using agentMain, the attach application and the agent can be two completely different applications.

  3. Classes called back to ClassFileTransformer in premain mode are all classes loaded by the virtual machine. This is determined by the order in which agents are loaded, which in developer logic means: The premain method is activated before all classes are first loaded and enter the program’s main() method, and all loaded classes execute the callbacks in the ClassFileTransformer list.

  4. Instrumentation#retransformClasses(Class<?) Instrumentation#retransformClasses(Class<? >… Classes) enables the corresponding class to be re-converted, thereby activating the re-converted class to perform callbacks in the ClassFileTransformer list.

  5. If the preMain proxy Jar is updated, you need to restart the server. If the AgentMain Jar is updated, you need to attach it again, but agentMain reattach also causes repeated bytecode insertion problems. However, there are Hotswap and DCE VM ways to avoid this.