方法区(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
。