方法区(Method Area)

同堆一样,方法区是线程共享的内存区域,用于存储被虚拟机加载的

类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

针对方法区的内存回收,主要是对常量池的回收和类型的卸载

方法区的实现

方法区的实现方式不受《Java虚拟机规范》约束,不要求统一。

  • JDK8之前HotSpot虚拟机使用堆的永久代实现
    • JDK7HotSpot将字符串常量池、静态变量移到堆中。
    • JDK8HotSpotJDK7中永久代剩余的内容(主要是类型信息)移到元空间中
  • JDK8HotSpot虚拟机使用元空间(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中上边的代码返回falsefalse

intern()方法将str1/str2实例复制到永久代的字符串常量池中,

返回的是永久代里的对象的地址,与当前堆中的str1/str2不同,因此为false


JDK7中将返回truefalse

intern()方法不需要复制字符串到永久代了,字符串常量池已经移到Java堆中,只需要在常量池里记录首次出现的实例引用即可。

str1是首次出现,因此str1.intern()返回的地址也是str1实例的地址,因此为true

str2中,由于Java这个字符在已经在常量池(在加载sum.misc.Version时进入常量池),

因此str2.intern()返回的地址不等于str1实例的地址,为false