[size=medium]先看一个实例
使用URLClassLoader每隔3秒钟重复加载A类的class
假如在这3秒钟内我们把class修改了
将会得到什么样的结果:[/size]
-------------------------------------
[size=medium]
以debug的方式运行、按照下面的修改后运行得到的结果
--------------------------------
1、修改成员变量的值------------动态打印修改后的值
2、修改成员方法的打印内容-------动态打印修改后的值
3、添加成员变量-----------提示警告
4、添加成员方法-----------提示警告
5、删除成员变量-----------提示警告
6、删除成员方法-----------提示警告
根据以上运行结果
[b]class类加载到虚拟机之后无法添加或者删除成员变量及成员方法
但是可以修改成员变量的值或者修改成员方法中的内容[/b]
[/size]
[size=large] 从另一个角度分析 [/size]
[size=medium]
从JVM结构的角度分析,class的结构信息存放在java的方法区内(method Area).包括常量池、字段描述、方法描述等等
常量池:用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池。
字段信息和方法描述:在编译时期就已经载入到方法区中。运行时无法对该部分的内容进行修改
方法区可以选择不实现垃圾收集、开源的JVM默认是不实现垃圾收集。
方法区是否需要实现垃圾收集其实是一个很难取舍的问题:
假如对方法区的内存进行垃圾收集、可以想象下次再实例化对象需要重新加载class信息无疑是加重了实例化对象的负担
假如不对方法区的内存进行垃圾收集、则无法动态修改一个class的结构;只能修改一些成员变量、成员方法的内容
相对一些热部署的应用将无法实现[/size]
使用URLClassLoader每隔3秒钟重复加载A类的class
假如在这3秒钟内我们把class修改了
将会得到什么样的结果:[/size]
-------------------------------------
package jvm;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) {
while (true) {
try {
ClassLoader parentLoader = Test.class.getClassLoader();
URLClassLoader loader1 = new URLClassLoader(new URL[] { null }, parentLoader);
Class<?> cls1 = loader1.loadClass("jvm.A");
A excel = (A) cls1.newInstance();
excel.print();
Thread.sleep(1000 * 3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package jvm;
public class A {
private String className = "class A";//修改成员变量 class A
static {
System.out.println(" static block initialization ");//修改静态成员变量的打印内容 static block initialization change
}
// private String addField = "field 1";//运行时添加一个成员变量
public void print() {
System.out.println("hello ");//修改方法内的打印内容hello change
System.out.println("------------------" + className);
System.out.println("the end");
}
//运行时添加一个成员方法
// public void print2() {
// System.out.println("hello ");//修改方法内的打印内容hello change
// System.out.println("------------------" + className);
// System.out.println("the end");
// }
}
[size=medium]
以debug的方式运行、按照下面的修改后运行得到的结果
--------------------------------
1、修改成员变量的值------------动态打印修改后的值
2、修改成员方法的打印内容-------动态打印修改后的值
3、添加成员变量-----------提示警告
4、添加成员方法-----------提示警告
5、删除成员变量-----------提示警告
6、删除成员方法-----------提示警告
根据以上运行结果
[b]class类加载到虚拟机之后无法添加或者删除成员变量及成员方法
但是可以修改成员变量的值或者修改成员方法中的内容[/b]
[/size]
[size=large] 从另一个角度分析 [/size]
[size=medium]
从JVM结构的角度分析,class的结构信息存放在java的方法区内(method Area).包括常量池、字段描述、方法描述等等
常量池:用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池。
字段信息和方法描述:在编译时期就已经载入到方法区中。运行时无法对该部分的内容进行修改
方法区可以选择不实现垃圾收集、开源的JVM默认是不实现垃圾收集。
方法区是否需要实现垃圾收集其实是一个很难取舍的问题:
假如对方法区的内存进行垃圾收集、可以想象下次再实例化对象需要重新加载class信息无疑是加重了实例化对象的负担
假如不对方法区的内存进行垃圾收集、则无法动态修改一个class的结构;只能修改一些成员变量、成员方法的内容
相对一些热部署的应用将无法实现[/size]