HotSwap and JRebel principle

HotSwap and JRebel principle

HotSwap和Instrumentation

In 2002, Sun introduced a new so-called HotSwapexperimental technique in the Java 1.4 JVM, which was synthesized in Debugger API-house and allowed the debugger to update the bytes of a class with the same class ID. code. This means that all objects can reference an updated class and execute new code when their methods are called, which avoids reloading the container whenever a class's bytecode is modified. this requirement. All modern IDEs (including Eclipse, IDEA, and NetBeans) support this technology, and starting with Java 5, this functionality is also provided Instrumentation APIdirectly to Java applications.

hotswap

Unfortunately, this redefinition is limited to modifying the method body - it can neither add methods or fields, nor modify anything other than the method body. This limits the usefulness of HotSwap, and it is made worse by several other problems:

Java compilers often create synthetic methods or fields, even if you are only modifying a method body (for example, when adding a class literal, anonymous and inner classes, etc.). Running in debug mode often slows down the application or introduces other problems.

These circumstances result in HotSwap being used less often than it should be.

Why is HotSwap limited to method bodies?

This question has been asked many times in the last 10 years since HotSwap was introduced. Among the bugs supporting JVM calls that make a whole set of changes, this is the bug with the highest votes, but so far it has not been implemented.

A disclaimer: I can't say I'm a JVM expert, I have a pretty good understanding of how the JVM is implemented in general, I've spoken to a handful of (former) Sun engineers over the years, but I haven't verified Every thing I say here. But having said that, I do have some ideas as to why this bug is still under development (although if you know the reason better, please correct me).

The JVM is heavily optimized software that runs on multiple platforms. Performance and stability are its highest priorities. To support these things in different environments, Sun's JVM provides the following features:

 两个重度优化的即时编译器(-client和-server)
 几个多代(multi-generational )垃圾收集器

These features make the development of class schemas a considerable challenge. To understand why, we need to take a closer look at what exactly is needed to support the addition of methods and fields (or even deeper, modifying the inheritance hierarchy).

When loaded into the JVM, an object is represented by an in-memory structure that occupies a contiguous region of memory of a certain size (its fields plus metadata). In order to add a field, we need to resize the structure, but since the adjacent area may already be occupied, we need to reallocate the entire structure into a different area with enough free space to fill it in Come in. Now, since we're actually updating a class (and not just an object), we have to do this for every object of that class.

This in itself is not difficult to achieve - the Java garbage collector is already doing the job of reallocating objects all the time. The problem is that an abstraction of a "heap" is just an abstraction. The actual layout of memory depends on the currently active garbage collector, and, to be compatible with all of these objects, reallocation should possibly be delegated to the active garbage collector. The JVM also needs to suspend during reallocations, so it makes sense that it does GC work concurrently during this time.

Adding a method does not require updating the structure of the object, but it does require updating the structure of the class, which is also reflected on the heap. But consider this situation: from the moment a class is loaded, it is essentially frozen forever. This enables JIT (Just-In-Time) to do the main optimization performed by the JVM - inlining. Most method calls in application hotspots are canceled, and the code is copied into the calling method. A simple check is inserted to ensure that the target object is indeed what we think it is.

So here comes the ridiculous thing: this "simple check" isn't enough when we can add methods to the class. What we need is a rather complicated check to make sure that no method with the same name is added to the target class and the target class's superclasses. In addition, we can also keep track of all inline points and their dependencies, and de-optimize them when the class is updated. There are two options to choose from, either at the cost of performance or higher complexity.

On top of that, given that we're talking about multiple platforms with different memory models and instruction sets, they may require more or less specific processing, so you're giving yourself an exorbitant cost without Too much ROI issue.

jrebel-agent

Introduction to JRebel

In 2007, ZeroTurnaround announced a tool called JRebel (JavaRebel at the time) that could update classes without the need for a dynamic class loader and with minimal restrictions. Unlike HotSwap which relies on IDE integration, the way this tool works is that it monitors the actual compiled .class files on disk and updates the class whenever a file is updated. This means that you can use JRebel with a text editor and a command line compiler if you want. Of course, it's also neatly integrated into Eclipse, InteliJ, and NetBeans. Unlike dynamic class loaders, JRebel preserves the identity and state of all existing objects and classes, allowing developers to continue using their applications without delay.

How to make it work?

For starters, JRebel works at a different level of abstraction than HotSwap. Given that HotSwap works at the virtual machine level and relies on the inner workings of the JVM, JRebel uses two notable features of the JVM - abstract bytecode and class loader. The class loader allows JRebel to identify the moment a class is loaded and then translate the bytecode in real time to create another layer of abstraction between the virtual machine and the executable code.

Others use this feature to provide profilers, performance monitoring, continuations, software transactional memory, and even a distributed heap. Combining bytecode abstraction with a class loader is a powerful combination that can be used to achieve a variety of functions that are more unusual than class overloading. As we dig deeper into this, we see that the challenge is not just in class overloading, but also in doing it without significant degradation in performance and compatibility matter,

As we reviewed in Reloading Java Classes 101, the problem with overloading classes is that once a class is loaded, it cannot be unloaded or changed; but we are free to load new ones if we want to. the type. To understand how we can overload classes in theory, let's look at dynamic languages ​​on the Java platform. Specifically, let's take a look at JRudy first (we've made a lot of simplifications so as not to torture anyone important).

Although JRuby uses "classes" as its features, at runtime, each object is dynamic, and new fields and methods can be added at any time. This means that a JRuby object is no different from a Map, with a mapping from method names to method implementations, and a mapping of domain names to their values. Implementations of these methods are contained in anonymous classes that are generated when methods are encountered. If you add a method, all JRuby has to do is generate a new anonymous class that contains the method's body. Because each anonymous class has a unique name, there is no problem loading the class, and as a result, the application is dynamically updated in real time.

In theory, since bytecode translation is usually used to modify the bytecode of a class, there is no reason why we can't use the information in a class, if only to create as many classes as needed to perform the function of the class . This way, we can use the same transformation as JRuby does to split all Java classes into holder classes and method body classes. Unfortunately, such an approach suffers from (at least) the following problems:

performance. Such a setup would mean that every method call would encounter a redirect. We can do optimizations, but the application will be at least an order of magnitude slower, and memory usage will skyrocket because so many classes are created. Java SDK classes . Classes in the Java SDK are significantly more difficult to work with than classes in an application or library. Also they are usually implemented in native code, so cannot be converted in a "JRuby" way. However, if we leave them as-is, then various incompatibilities will be thrown that may not be able to be circumvented. compatibility. Although Java is a static language, it includes some dynamic features such as reflection and dynamic proxies. If we use "JRuby"-style transformations, these features will fail unless we replace the Reflection API with our own classes that know what transformations to do.

Therefore, JRebel did not adopt such an approach. Instead, it uses a more sophisticated approach, based on advanced compilation techniques, leaving us with a main class and several anonymous support classes backed by the JIT's transformation runtime, which allow modifications to be made without There will be any noticeable performance or compatibility degradation. it also

留有尽可能多完整的方法调用,这意味着JRebel把性能开销降低到了最小,使其轻量级化。
避免了改编(instrument)Java SDK,除了少数几个需要保持兼容性的地方外。
调整Reflection API的结果,这样我们就能够把这些结果中已添加/已删除的成员正确地包含进来。这也意味着注解(Annotation)的改变对于应用来说是可见的。

In addition to class overloading - and archives

Overloading classes is something that Java developers have complained about for a long time, but once we solved it, other problems followed.

The development of the Java EE standard has not paid much attention to the turnaround of development (the time it takes to make a change to the code and observe the impact of the change in the application). The idea is that all applications and their modules are packaged into archives (JARs, WARs, and EARs), which means that before you can update any files in the application, you need to update the archive - which is usually An expensive operation involving build systems such as Ant or Maven. As we discussed in Reloading Java Classes 301, it is possible to minimize overhead by using expanded development and incremental IDE builds, but for large applications this is usually not a viable option.

To solve this problem, in JRebel 2.x, we developed a way for users to map archived applications and modules back to the workspace - users create a rebel.xml configuration in each application and module file, which tells JRebel where to find the source files. JRebel is integrated with the application server, and when a class or resource is updated, it is read from the workspace rather than from the archive.

workspace-map

This approach allows instant updating of not only classes, but any type of resource such as HTML, XML, JSP, CSS, .properties, etc. Maven users don't even need to create a rebel.xml file, as the Maven plugin will generate it automatically.

Beyond class overloading - configuration and metadata

In the process of eliminating the turnaround period, another problem became apparent: Today's applications are not just classes and resources, they are bound together by a lot of configuration and metadata. When configuration changes are made, the changes should be reflected in that running application. However, it is not enough to make changes to the configuration file visible, the specific framework must reload the configuration to reflect the changes in the application.

conf

To support these types of changes in JRebel, we have developed an open source API that allows our team and third-party contributors to use JRebel's features using framework-specific plugins to propagate changes made in the configuration into the frame. For example, we support adding beans and dependencies in Spring on the fly, as well as supporting various changes made in other frameworks.

in conclusion

This article summarizes the various ways of overloading Java classes without using a dynamic class loader. We also discuss what causes HotSwap's limitations, reveal how JRebel works behind the scenes, and discuss other issues that arise when addressing class overloading.

Original address: http://article.yeeyan.org/view/213582/186226

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326132470&siteId=291194637