方法区(Method Area)
同堆一样,方法区是线程共享的内存区域,用于存储被虚拟机加载的
类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
针对方法区的内存回收,主要是对常量池的回收和类型的卸载
方法区的实现
方法区的实现方式不受《Java虚拟机规范》约束,不要求统一。
JDK8之前HotSpot虚拟机使用堆的永久代实现JDK7的HotSpot将字符串常量池、静态变量移到堆中。JDK8的HotSpot将JDK7中永久代剩余的内容(主要是类型信息)移到元空间中
JDK8的HotSpot虚拟机使用元空间(Meta Space)实现
永久代的概念
永久代和方法区并不等价,只是JDK8之前HotSpot虚拟机的设计团队使用永久代来实现方法区而已,
使得HotSpot的垃圾收集器能够像管理Java堆一样管理方法区。
运行时常量池(Runtime Constant Pool)
方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,
还有一项就是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,
这部分内容将在类加载后存放到方法区的运行时常量池中。
题
public static void main(String[] args) {
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}
String::intern()是一个本地方法,作用是如果字符串常量池中已经包含一个等于此String对象的字符串,
则返回池中的对象的引用;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的应用。
JDK6中上边的代码返回false、false,
intern()方法将str1/str2实例复制到永久代的字符串常量池中,返回的是永久代里的对象的地址,与当前堆中的
str1/str2不同,因此为false。
JDK7中将返回true、false,
intern()方法不需要复制字符串到永久代了,字符串常量池已经移到Java堆中,只需要在常量池里记录首次出现的实例引用即可。
str1是首次出现,因此str1.intern()返回的地址也是str1实例的地址,因此为true。
str2中,由于Java这个字符在已经在常量池(在加载sum.misc.Version时进入常量池),因此
str2.intern()返回的地址不等于str1实例的地址,为false。