您的位置:首页 > 其它

JVM 内存基础概念之 方法区和运行时常量池

2016-02-23 16:28 127 查看


方法区和运行时常量池


方法区

方法区和 Java 堆一样,是被所有线程所共享的一块内存区域。 方法区的作用是存储已经被 JVM 加载到方法区之中的 Java 类的类型信息。 前面我们多次提到了类的实例数据和类型数据,实例数据指的是在类中定义的各种实例对象以及它们的值。 类信息值的是定义在类中的常量、静态变量、以及类中所申明的各种方法、字段等等。还包括了即时编辑器编译之后产生的代码数据。

在 JVMS 之中,还给方法区起了个别名:“非堆”。目的就是为了与 JAVA 堆区分开来。并且 JVMS 不要求该区域实现自动内存管理,但是商用的 JVM 都能够自动管理该区域的内存。

方法区中定义了 OutOfMemoryError 异常。


运行时常量池

运行时常量池属于方法区的一部分,它同方法区一样是全局共享的,其作用是存储 Java 类文件常量池中的符号信息。该区域可能会发生 OutOfMemoryError 异常。


永久代与方法区

在 JDK 1.2 ~ JDK 6,HotSpot 使用永久代实现方法区。 从 JDK 7 开始, HotSpot 开始了移除永久代计划。其中符号表被移到 Native Heap 中,字符串常量和类的静态引用被移到 Java Heap 中。 在 JDK 8 中,永久代已被元空间(Metaspace)所替代。

HotSpot 方法区的迁移过程,除了对内存溢出时使用的调试工具、调试手段产生影响之外。它对 Java 代码的运行也会产生一些很轻微的变化。

下面来看一个小例子并来分析其变化:
public class Main {
public static void main(String[] args) {
String s1 = new StringBuilder("Air").append("said").toString();
System.out.println(s1.intern() == s1);// 在 JDK 1.6 或之前返回 false, 1.7 或以后返回 true

String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2);// 一直返回 false
}
}


首先来了解下 intern() 方法的作用:
在 1.7 之前:该方法会去通过 equals() 方法判断,如果字符串常量池中已经有了等于该 String 对象的字符串,则直接返回常量池中的地址引用,没有,则会 copy 字符串到常量池,并返回常量池中的地址引用。
从 1.7 开始:intern() 方法会首先去字符串常量池中查询是否存在,如果存在,则直接返回常量池中的地址引用。但是,如果在常量池中找不到,则不会再将字符串 copy 到常量池,而会在常量池中生成一个对堆中原字符串的引用。

以 JDK1.6 运行如上代码,会得到 false、false 的结果。那我们就先来分析下 JDK1.6 时上面的代码都做了些什么。 由于在 JDK1.7 之前,字符串常量池是放在方法区当中的,所以 s1.intern() 返回的是方法区常量池中的引用地址,而 s1 则是 Java 堆当中的地址。所以结果为 false。s2 结果同上。

再以 JDK1.7 运行如上代码,会得到 true、false 的结果。先来看 s1 为什么会是 true,因为“Airsaid” 是首次出现的字符串,并且通过
new StringBuilder("Air").append("said").toString()


的方式只会在常量池中生成 "Air" 和 "said" 两个常量,并没有 "Airsaid" 于是 s1.intern() 返回的是堆中的引用,两个都是堆中的引用地址,于是结果为 true。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: