Explore Java Thermal Deployment III (Java Agent Agentmain)

Source: Internet
Author: User

Objective

Let's continue our quest for Java thermal deployment. In the previous quest for Java Thermal Deployment II (Java Agent Premain), we introduced the Java Agent Premain. By adding the Premain method before the main method through an AOP-like approach, we can modify the bytecode before the class is loaded, whether it is the first load, or every new ClassLoader load, it goes through Classfiletransformer Transform method, that is, you can modify the bytecode in this method, although his method name is Premain, but we can actually use this feature to do this thing.

At the end of the article, we also mentioned that, although compared to modifying bytecode in a custom class, Premain is not intrusive and transparent to the business, but the drawback is that he also needs to add parameters at startup.

We also mentioned that although premain can be hot-deployed, it also requires a re-creation of the ClassLoader, although this indeed conforms to the JVM's definition of the uniqueness of the class. However, there is a case where we cannot create a new ClassLoader object if you are using the system ClassLoader. Then we can not reload the class, how to do? Fortunately Java 6 provides us with a way to Agentmain today's protagonist.

1. What is Agentmain?

With the Premain, we know that Premain can only modify the bytecode before the class is loaded, and after the class is loaded, it can only be reloaded by re-creating the ClassLoader. And Agentmain was born to compensate for this shortcoming. In short, agentmain can load a class again after the class is loaded, that is, redefine, you can modify the class at the time of redefinition, or even create a new ClassLoader, and the JVM has internally redefined the class (the process of redefining it is rather complicated).

However, the modification of the class by this way is limited by contrast to the original old class, by the following requirements:

1. The parent class is the same;

    1. Internship that the number of interfaces must be the same;
    2. Class accessors must be consistent;
    3. The number of fields and field names must be the same;
    4. The new method must be private static/final;
    5. You can delete the modification method;

Can be seen, compared with the re-creation of the class loader, the limit is quite a lot of, the most important fields can not be modified. Therefore, use the time to pay attention.

However, Agentmain also has a strong feature, is that the target program does not need to tube, you can be agents. Remember how Premain was used? The-javaagent parameter needs to be added when the target application is started. Although not invasive, but compared to Agentmain, there is still invasive, after all, agentmain nothing. The target program runs independently, without any control.

Then let's try it!

2. How do I use it?

Agentmain Use the following steps:

    1. Define a MANIFEST.MF file, the file must contain agent-class;
    2. Creates a agent-class specified class that must contain the Agentmain method (parameters and Premian are the same).
    3. MANIFEST.MF and Agent classes into jar packages;
    4. Load the jar package into the target virtual machine. The target virtual machine will automatically execute the method logic of the Agentmain method, while the Classfiletransformer will be valid for a long time, and will be intercepted when each class loader loads class.

Let's follow the steps to do it again!

    1. Define a MANIFEST.MF file, the file must contain agent-class;
Manifest-Version: 1.0Can-Redefine-Classes: trueAgent-Class: cn.think.in.java.clazz.loader.asm.agent.AgentMainTraceAgent Can-Retransform-Classes: true
    1. Creates a agent-class specified class that must contain the Agentmain method (parameters and Premian are the same).
 Public classagentmaintraceagent { Public Static void Agentmain(String Agentargs, Instrumentation Inst)throwsunmodifiableclassexception {System. out.println("Agent Main called"); System. out.println("Agentargs:"+ Agentargs); Inst.Addtransformer(NewClassfiletransformer () {@Override       Public byte[]Transform(ClassLoader loader, String className, class<?> classbeingredefined, Protectiondomain Protectiondomain,byte[] classfilebuffer)throwsillegalclassformatexception {System. out.println("Agentmain load Class:"+ ClassName);returnClassfilebuffer; }    },true); Inst.retransformclasses(account.class); }

The class logic above is very simple, print the Agent Main called, and print the parameters, then add a class converter, the logic in the converter is just the print string, for the sake of simplicity, and do not modify the bytecode (you can use ASM and other class library modifications). Finally, it is important to implement the inst.retransformclasses (Account.class); This code means to re-convert the target class, which is the account class. That is, you need to redefine which class to specify, otherwise the JVM cannot know. There is a similar approach to redefineclasses, note that this method is used before the class is loaded. You need to use the Retransformclasses method after the class is loaded.

    1. MANIFEST.MF and Agent classes into jar packages;
      This please google for yourself. Either maven or the IDE can.
    2. Load the jar package into the target virtual machine. The target virtual machine will automatically execute the method logic of the Agentmain method, while the Classfiletransformer will be valid for a long time, and will be intercepted when each class loader loads class.

This piece of code is important:

classJvmtithread { Public Static void Main(string[] args)throwsIOException, Attachnotsupportedexception, Agentloadexception, agentinitializationexception {List< virtualmachinedescriptor> list = Virtualmachine.List(); for(Virtualmachinedescriptor vmd:list) {if(VMD.)DisplayName().EndsWith("Accountmain") {Virtualmachine Virtualmachine = virtualmachine.Attach(VMD.)ID()); Virtualmachine.loadagent("E:\\ Self\\Demo\\ out\\Artifacts\\Test\\Test.jar ","Cxs"); System. out.println("OK"); Virtualmachine.Detach(); }    }  }}

Note: When writing this code, the IDE may be prompted not to find the jar package, this time will jdk/lib/tools.jar add the project's classpath, specifically please google.
The main method steps are as follows:

    1. Gets all the virtual machines of the current system, similar to the JPS command.
    2. Cycle through all virtual machines and locate the Accountmain virtual machine.
    3. Links the current JVM to the target JVM and loads the Loadagent jar package and passes the parameters.
    4. Uninstall the virtual machine.

How to test it?

First look at the test class, which is the Accountmain class:

class AccountMain {  publicstaticvoidmainthrows InterruptedException {    for (;;) {      newAccount().operation();      Thread.sleep(5000);    }  }}

When everything is ready, start Accountmain, and then start the Jvmtithread class, the results are as follows:

As you can see, after executing the operation Method 1 times, we started the attach JVM, and then the Agentmain method was executed, and we printed the string arguments we passed, and also entered the Classfiletransformer method, Indicates that the account class was reloaded. It is a bit regrettable that we did not modify the bytecode, confined to space. But the landlord still show the landlord to modify the results of the bytecode, if the landlord wants to count the length of the operation method, the landlord will use ASM to add a period of statistical code:

 Public classTimestat {StaticThreadlocal<long> ThreadLocal =NewThreadlocal<> (); Public Static void Start() {threadLocal.Set(System.Currenttimemillis()); } Public Static void End() {LongTime = System.Currenttimemillis()-threadLocal.Get(); System. out.Print(Thread.CurrentThread().Getstacktrace()[2] +"method is time consuming:"); System. out.println(Time +"milliseconds"); }}

Then modify the Classfiletransformer transform logic in the Agentmain method, which is where the bytecode is modified.
The results of the operation are as follows:

As you can see, the account class is redefined first, the Timestat class is actively loaded, and then it takes effect to print out the time-consuming method behind the operation string.

Summarize

Through the use of agentmain, we feel his strong, in the target program does not change, even if the startup parameters are not added, you can modify the class, and is modified after the run, and do not re-create the class loader. Its main benefit from the JVM's bottom-level redefinition of the class, about the underlying code interpretation, the JVM great God Cold Spring Son has an article on the JVM source code Analysis of the javaagent principle of full interpretation, detailed analysis of the principle of javaagent. However, Agentmain has some functional limitations, such as the field cannot be modified or decreased. Therefore, the use of the time needed to weigh, in the end use the way to achieve the hot deployment.

Said so long hot deployment, is actually the dynamic or say the runtime modifies the class, the big direction says has 2 kinds of ways:

    1. With Agentmain, you do not need to recreate the class loader, you can modify the class directly, but there are many limitations.
    2. The Premain can be modified before the class is first loaded, and the class loader needs to be recreated after the load is modified. Or in a custom class loader type of modification, but this method compares coupling.

Either way, you need a library of bytecode modifications, such as Asm,javassist, Cglib, and many more. In short, through the Java.lang.instrument package with bytecode library, can be very convenient to dynamically modify the class, or thermal deployment.

Good luck!!!!!

Reference: The javaagent principle of JVM source analysis is fully understood
Java Virtual Machine in action
Javaagent
Instrumentation new features

Explore Java Thermal Deployment III (Java Agent Agentmain)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.