Reloading a bytecode in a virtual machine when application is running is very limited. In fact HotSpot(TM) VM allows only changing method bodies. To address this problem some commercial and open source tools were created. Among them is Dynamic Code Evolution Virtual Machine (DCEVM) and HotSwapAgent - very promising open source tool.
I have already some experience in using DCEVM. Some times ago I have been working for an insurance company where I was using this modified vm to develop a code in a gosu language. Gosu is another JVM language. I remember that then hot swapping worked very well.
Let’s try this tool. First we need to patch our current jvm.
Installing Dynamic Code Evolution VM
In order to enhance current Java (JRE/JDK) installations with DCEVM you need to download the latest release of DCEVM installer for a given major java version,
java 7
andjava 8
are supported
run the installer
1
|
|
then select a proper java installation directory on your disc and press Install DCEVM as altjvm
That’s all really. Very simple isn’t it ?
To validate the installation run:
1
|
|
and if everything went alright you should see something similar to below output:
1 2 3 |
|
Note that in the third line instead of Java HotSpot(TM)
we have
now Dynamic Code Evolution
.
Kind of installers
Worth noting is the fact that there are two kind of installers
- light
- and full
The latter one supports more features (for example, it supports removal of superclasses), but because of the maintenance issues the full edition is available for a fewer versions of jdk.
Downloading HotswapAgent
HotswapAgent does the work of reloading resources and framework configuration.
So in order to have a support for reloading a spring bean definitions just
after a change occurs, we need to perform one more step -
download latest release of hotswap-agent.jar
and put it anywhere. For example here: ~/bin/hotswap/hotswap-agent.jar
.
Running application in order to test hot swapping
I will use Main
and Main2
classes to play with hot swapping:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
And the second one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
I will test following use cases:
case | works ? | Test class | |
---|---|---|---|
1. | change body method | YES | Main |
2. | add method | YES | Main |
3. | add field | YES | Main |
4. | remove field | YES | Main2 |
5. | remove method | YES | Main2 |
Intellij IDEA settings
All tests will be performed using Intellij IDEA. Ensure that following options are set
- enable classes reloading
- pass
-XXaltjvm=dcevm
vm option to run/debug configuration
Case 1 : change body method
Run debug. In the console you should see following output:
1 2 3 4 |
|
Then change counter++;
to counter+=2;
in Foo
class.
1 2 3 4 5 6 7 8 |
|
Hit <ctr>+<shift>+<F9>
to compile and after few seconds you should spot that the classes were reloaded successfully
1 2 3 |
|
Case 2 : add method
Revert all changes in Main
class and run debug. Add method
1 2 3 |
|
to the Foo
class and call it from the mainLoop
1 2 3 4 5 6 |
|
Hit <ctr>+<shift>+<F9>
to compile.
1 2 3 4 5 |
|
Classes were reloaded successfully.
Case 3 : add field
Revert all changes in Main
class and run debug. Add field int counter2
to Foo
class and append following
two statements to the end of foo
method.
1 2 |
|
Foo
class should look following
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Hit <ctr>+<shift>+<F9>
to compile. And appears
1 2 3 4 5 |
|
that this kind of change was also reloaded successfully.
Case 4 : remove field
Run debug. In the console you should see following output:
1 2 3 |
|
Then add two public fields int number
and String name
to the Foo
class.
1 2 3 4 |
|
Hit <ctr>+<shift>+<F9>
to compile, and after few seconds…
1 2 3 |
|
Then remove number
field and hit again <ctr>+<shift>+<F9>
.
1 2 3 |
|
The change was reloaded.
Case 5 : remove method
Revert all changes in Main2
class and run debug. Add
- public field
String name
- public method
String getName()
to the Foo
class. Then hit <ctr>+<shift>+<F9>
to compile, and after few seconds…
1 2 3 |
|
Then remove getName()
method and hit again <ctr>+<shift>+<F9>
.
1 2 3 |
|
Seems that this change was also reloaded successfully.
Notes
During the play with hot swapping in Intellij IDEA you could notice that for some circumstances code would not be reloaded. Intellij IDEA has a following limitation about which you need to be aware:
the old code is still used until the VM exits the obsolete stack frame
About that you can read here