1.方法区介绍
方法区与JAVA堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区是虚拟机规范中的一部分,描述为堆的一个逻辑部分。
永久代和元空间都是实现方式。
2.迁移变化详解
- JDK 6:有去掉永久代的计划,但还未实施。
- JDK 7:先迁移一部分,把常量池和静态变量移到JAVA堆中,保留永久代的实现。
- JDK 8:把常量池继续留在JAVA堆中,而把其他的几个东西放入到元空间中,从此诞生元空间的说法,移除永久代的概念。
在迁移变化过程中,方法区的核心概念从来没变过,但是内部存储的实现发生了变化。
例如:
在JDK6及以前,常量池溢出时,报
java.lang.OutOfMemoryError:PerGen space
在JDK7及以后,常量池溢出时,报
java.lang.OutOfMemoryError:Java heap space
3. 其他相关知识
3.1 String.intern()
JavaDoc介绍:
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.
也就是说:
String.intern方法首先会尝试从字符串常量池中取出字面量相同的字符串对象返回,如果没有字符串常量池中没有的话,则会该字符串对象加到池中(JDK6是拷贝,JDK7是引用),然后返回池中该字符串对象的引用(而原来的字符串对象没有销毁)。
面试题:在JDK6中输出结果
public class StrTest {
public static void main(String[] args) {
String str1 = new StringBuilder("编程是个").append("有趣的事情").toString();
System.out.println(str1.intern() == str1); // false
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str1); // false
}
}
面试题:在JDK7中输出结果
public class StrTest {
public static void main(String[] args) {
String str1 = new StringBuilder("编程是个").append("有趣的事情").toString();
System.out.println(str1.intern() == str1); // true
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str1); // false
}
}
分析:在JDK6中运行,会得到两个false,而在JDK7中运行,会得到一个true和一个false。产生差异的原因是,在JDK6中,inter()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果讲返回false。
而JDK7的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆了,拿只需要再常量池里记录以下首次吹西安的实例引用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false,这是因为"java"这个字符串在执行StringBuilder.toString()之前就已经出现过了,字符串常量池中已经有它的引用,不符合intern()方法要求"首次遇到"的原则。