Explore Java hot deployment in depth

Source: Internet
Author: User

In the Java development field, hot deployment has always been a difficult problem, the current Java virtual machine can only implement the method body modification hot deployment, for the entire class structure modification, still need to restart the virtual machine, the class reload to complete the update operation. For some large applications, each reboot will cost a lot of time. The advent of the OSGi architecture makes it possible to restart the module, but if there is a call relationship between the modules, such operations will still cause transient functional shock to the application. This article explores how to implement a single-class thermal deployment without destroying the existing behavior of the Java virtual machine, so that the system does not need to reboot to complete a class update.

Exploration of class loading

First of all, what is hot deployment (HOTSWAP), a hot deployment is the ability to automatically detect changes to a class file without restarting the Java virtual machine, updating the behavior of the runtime class. Java classes are loaded by a Java virtual machine, and a class file is ClassLoader loaded, the corresponding class object is generated, and then an instance of the class is created. The default virtual machine behavior only loads the class at startup, and if a later class needs to be updated, simply replacing the compiled class file, the Java virtual machine will not update the running class. if you want to implement a hot deployment, the most fundamental way is to modify the virtual machine's source code, change the load behavior of the ClassLoader, so that the virtual function listens to the class file update, reload the class file, this behavior is very destructive, for the subsequent JVM upgrade buried a big pit.

Another friendly approach is to create your own classloader to load the class that needs to be monitored, so that you can control when the classes are loaded, allowing for a hot deployment. This article will specifically explore how to implement this scenario. The first step is to understand the existing loading mechanism of the Java virtual machine. The current loading mechanism, called Parental delegation , asks whether the parent class of the current ClassLoader has the ability to load when using a ClassLoader to load the class, and if the parent class cannot implement the load operation, the task is delegated to the ClassLoader To load. The benefit of this top-down loading method is that each classloader performs its own load task and does not load the class repeatedly. but this makes the loading order very difficult to change, so that custom ClassLoader preemptive loading of classes that need to listen for changes becomes a challenge.

But we can change the idea, although we can't get the first load of the class, but you can still create a class with the same functionality as the custom ClassLoader, so that each instantiated object points to the new class. When the class file changes, create an updated class again, and then if the system makes an instantiation request again, the created object points to the new class.

Here's a brief list of what needs to be done.

Create a custom ClassLoader, load the class that needs to listen for changes, and reload the class file when it changes.

Change the behavior of the created objects so that they are created with the custom ClassLoader loaded class.

Implementation of the custom loader

The custom loader still needs to perform the function of class loading. There is a problem here, the same ClassLoader cannot load two classes of the same name at the same time, because no matter how the structure of the class changes, the generated class name does not change, and ClassLoader can only destroy the loaded class before the virtual machine stops. so that the ClassLoader cannot load the updated class. Here's a little trick to keep each loaded class stored as a type with version information, like when loading Test.class, the class stored in memory is Test_v1.class, and when the class changes, the reload class name is Test_v2.class. But the DefineClass method that actually executes the load class file to create the class is a native method that becomes difficult to modify. So there is still a road ahead, that is, directly modify the compiled generated class file.

Modifying class files with ASM

There are many frameworks that can modify bytecode, such as Asm,cglib. This article uses ASM. To introduce the structure of the class file, the class file contains the following types of information:

The first is the basic information of the class, including access rights information, class name information, parent class information, interface information.

The second one is the variable information for the class.

The third one is information about the method.

ASM loads a class file and then reads the class information in strict order, and the user can define the enhanced component to modify the information and then output it as a new class.

First look at how ASM can be used to modify class information.

Listing 1. Using ASM to modify byte codes

Classwriter CW =NewClasswriter (CLASSWRITER.COMPUTE_MAXS); Classreader CR=NULL; String Enhancedclassname=classsource.getenhancedname ();Try{CR=NewClassreader (NewFileInputStream (Classsource.getfile ()));} Catch(IOException e) {e.printstacktrace ();return NULL;} Classvisitor CV=NewEnhancedmodifier (Cw,classname.replace (".", "/"), Enhancedclassname.replace (".", "/") ); Cr.accept (CV,0);

ASM the process of modifying bytecode files is a chain of responsibility mode, first using a classreader to read the bytecode, and then use Classvisitor to make personalized changes, and finally use Classwriter output modified bytecode.

As mentioned before, the class name of the read class file needs to be modified to be loaded into a derived class with a completely new name. This is divided into 2 steps.

The first step is to turn the original class into an interface first.

Listing 2. Redefining the original class

 PublicClass<?>Redefineclass (String className) {classwriter CW=NewClasswriter (CLASSWRITER.COMPUTE_MAXS); Classreader CR=NULL; Classsource CS=Classfiles.get (className);if(cs==NULL){return NULL;}Try{CR=NewClassreader (NewFileInputStream (Cs.getfile ()));} Catch(IOException e) {e.printstacktrace ();return NULL;} ClassModifier cm=NewClassModifier (CW); cr.accept (cm,0);byte[] Code =Cw.tobytearray ();returnDefineClass (className, code, 0, code.length);}

First load the class file of the original classes, where an enhanced component classmodifier is defined to modify the type of the original class and convert it to an interface. All method logic for the original class is removed.

In the second step, the generated derived classes implement this interface, the original class, and copy all the method logic from the original class. later, if the class needs to be updated, a new derived class is generated, and this interface is implemented. The purpose of this is to have a common interface between the derived classes of the same class, regardless of how they are modified, and the transitions between them become opaque to the outside.

Listing 3. Defining a derived class

//redefine this class when a class file changesPrivateClass<?>Redefineclass (String className, Classsource classsource) {classwriter CW=NewClasswriter (CLASSWRITER.COMPUTE_MAXS); Classreader CR=NULL; Classsource.update (); String Enhancedclassname=classsource.getenhancedname ();Try{CR=NewClassreader (NewFileInputStream (Classsource.getfile ()));} Catch(IOException e) {e.printstacktrace ();return NULL;} Enhancedmodifier em=NewEnhancedmodifier (CW, Classname.replace (".", "/"), Enhancedclassname.replace (".", "/")); Extendmodifier EXM=NewExtendmodifier (EM, Classname.replace (".", "/"), Enhancedclassname.replace (".", "/")); Cr.accept (EXM,0);byte[] Code =Cw.tobytearray (); classsource.setbytecopy (code); Class<?> clazz = defineclass (enhancedclassname, code, 0, code.length); Classsource.setclasscopy (clazz);returnClazz;}

Load the original class file again, where two enhancements are defined, one is enhancedmodifier, and the function of this enhanced component is to change the original class name. The second enhancement component is extendmodifier, the function of which is to change the parent class of the original class so that the modified derived class can implement the same original class (at which point the original class has been turned into an interface).

Custom ClassLoader Another function is to listen to the class file that will change, ClassLoader will manage a timer and periodically scan these class files for changes.

Changing the behavior of creating objects

There are two common ways to create objects in a Java virtual machine, one that is statically created, a direct new object, a dynamic creation, and a reflection method to create an object.

Since the type of the original class has been changed in the custom loader and changed from class to interface, neither of these methods can be created. What we want to do is to instantiate the behavior of the original class into an instantiated derived class.

The first way to do this is to create it statically, change it to get the class by ClassLoader, and create the object dynamically.

Listing 4. The logic corresponding to the replaced instruction set

Primitive logic

Greeter p = new Greeter ();

Post-change logic

Igreeter p = (igreeter) myclassloader.getinstance ().

Findclass ("Com.example.Greeter"). newinstance ();

ASM is also needed to modify the class file. Finds statements for all new objects, replaced by ClassLoader to get the form of an object.

Listing 5. Using ASM to modify the method body

@Override Public voidVISITTYPEINSN (intopcode, String type) {if(Opcode==opcodes.new &&type.equals (ClassName)) {List<LocalVariableNode> variables =Node.localvariables; String Compiletype=NULL; for(intI=0;i<variables.size (); i++) {Localvariablenode localvariable=Variables.get (i); Compiletype=FormType (LOCALVARIABLE.DESC);if(MatchType (Compiletype) &&!Valiableindexused[i]) {Valiableindexused[i]=true; Break;}} MV.VISITMETHODINSN (opcodes.invokestatic, Classload_type,"GetInstance", "() L" +classload_type+ ";"); MV.VISITLDCINSN (Type.replace ("/", "."); MV.VISITMETHODINSN (Opcodes.invokevirtual, Classload_type,"Findclass", "(ljava/lang/string;) Ljava/lang/class;"); MV.VISITMETHODINSN (Opcodes.invokevirtual,"Java/lang/class","Newinstance", "() Ljava/lang/object;"); MV.VISITTYPEINSN (Opcodes.checkcast, compiletype); flag=true;} Else{mv.visittypeinsn (opcode, type);}}

For the second method of creation, you need to make them load the class through the custom loader by modifying the behavior of Class.forName () and Classloader.findclass ().

Using javaagent to intercept the behavior of the default loader

The previously implemented ClassLoader has solved the functionality required for hot deployment, but when the JVM starts, it does not load all the class files under Classpath with a custom loader, instead of loading them with the application loader. If you later load the loaded class with a custom loader, you may have linkageerror exception. You must re-replace the loaded class before the app starts. If there is only one method to use before jdk1.4, change the loading behavior of ClassLoader in the JDK to point to the load behavior of the custom loader. Fortunately, after jdk5.0, we have another aggressive smaller approach, this is the Javaagent method, Javaagent can be launched after the JVM, the application before the start of a short gap, provide space for the user to do some special behavior. The more common application is to use javaagent to do aspect-oriented programming, and to add monitoring logs to the methods.

The implementation of Javaagent is easy, as long as within a class, define a Premain method.

Listing 6. A simple javaagent.

 Public class reloadagent {publicstaticvoidnew  Generaltransformer (); Inst.addtransformer (trans);}}

Then write a manifest file, set the Premain-class property to define a class name that has the Premain method.

Generate a jar package containing this manifest file.

manifest-version:1.0premain-Class:com.example.ReloadAgentCantrue

Finally, you need to add the-javaagent parameter to the application's parameters to add the jar. You can also add parameters to the Javaagent parameter, which is the absolute path to test project in the testing code. This will take precedence over the logic in the Premain method before executing the application, and pre-parse the class that needs to be loaded.

This replaces the original bytecode with javaagent, preventing the original bytecode from being loaded by the Java virtual machine. Just implement a Classfiletransformer interface and use this implementation class to complete the function of class substitution.

Listing 7. Replace class

@Override Public byte[] Transform (ClassLoader paramclassloader, String paramstring,class<?>Paramclass, Protectiondomain Paramprotectiondomain,byte[] paramarrayofbyte)throwsillegalclassformatexception {String className= Paramstring.replace ("/", "."));if(Classname.equals ("Com.example.Test") {Myclassloader cl=myclassloader.getinstance (); Cl.definereference (ClassName,"Com.example.Greeter");returnCl.getbytecode (className);}Else if(Classname.equals ("Com.example.Greeter") {Myclassloader cl=myclassloader.getinstance (); Cl.redefineclass (className);returnCl.getbytecode (className);}return NULL;}

At this point, all the work is done, and enjoy the results of hotswap.

Explore Java hot deployment in depth

Related Article

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.