java内存运行时数据区域及其含义
1.程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
2.与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame[1])用于存储局部变量表、 操作数栈、 动态链接、 方法出口等信息。 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、 char、short、 int、float、long、 double)、 对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。 局部变量表所需要的内存空间在编译期间完成。如果线程请求的栈深度大于虚拟机所允许的最大深度,那么将抛出StackOberFlowError异常,如果虚拟机在拓展栈是无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
3.本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
4.堆:在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 同时也是垃圾收集器管理的主要区域,又被称为GC堆(Garbage Collected Heap)
5.方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据。
6.运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、 字段、 方法、 接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
java对象的创建基本过程
java对象的内存布局
1.对象内存布局:对象头(Mark Word,类型指针--指向类元数据以确定这个类是哪个类的实例,数组长度),实例数据、对齐填充
对象的访问定位
OutOfMemoryError异常
JDK1.6中的intern:
调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用;如果不存在的话,则会将原先堆中的该字符串拷贝一份到常量池中。 一定注意是拷贝!
JDK1.7中的intern:
调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用;如果不存在的话,则只会将原先堆中该字符串的引用放置在常量池中,并不会将拷贝整个字符串到常量池中。 一定注意是引用!
这也就说明,JDK1.6和JDK1.7对于常量池中不存在此字符串的情况处理不同。
实例:
public static void main(String[] args) {
String str = "str"+new String("01");①
str.intern();②
String str1 = "str01";③
System.out.println(str == str1);
String str2 = new String("str01");④
str2.intern();⑤
String str3 = "str01";⑥
System.out.println(str2 == str3);
String str4 = "str01";⑦
String str5 = new String("str")+new String("01");⑧
str5.intern();⑨
System.out.println(str4 == str5);
}
在JDK1.6下输出结果是:
false
false
false
解释:
①执行时会在堆内存创建一个值为"str01"的字符串对象str,同时在常量池创建一个"str"以及"01"常量;
②执行时会首先去常量池中查看是否存在一个值为"str01"的常量,发现不存在,JDK1.6的做法就是将该字符串"str01"在常量池中也生成一份;
③执行时会在常量池中创建一个"str01"对象,发现已经存在,因而不会新建;
第一个输出false的原因是:str指向的是堆内存的"str01",而str1指向的是常量池中的"str01";
④执行时会在堆内存创建一个值为"str01"的字符串对象str2,同时在常量池中创建一个值为"str01"的常量;
⑤执行时会首先去常量池中查看是否存在值为"str01"的常量,发现存在,则直接返回这个常量引用;
⑥执行时会在常量池中创建一个值为"str01"的常量,如果发现已经存在,则不会创建;
第二个输出false的原因是:str2指向的是堆内存的"str01",而str3指向的是常量池中的"str01";
⑦执行时会在常量池创建一个值为"str01"的常量;
⑧执行时会在堆内存创建一个值为"str01"的字符串对象str5,同时在常量池创建一个"str"以及"01"常量;
⑨执行时会去常量池查看是否存在值为"str01"的常量,发现存在则直接返回这个常量引用;
第三个输出false的原因是:str5指向的是堆内存的"str01",而str4指向的是常量池中的"str01";
在JDK1.7下输出结果是:
true
false
false
解释:
发现只有第一个输出结果不一样,所以我们只解释第一个的原因:
①执行时会在堆内存创建一个值为"str01"的字符串对象str,同时在常量池创建一个"str"以及"01"常量;(这点和JDK1.6没什么区别)
②执行时会首先去常量池中查看是否存在一个值为"str01"的常量,发现不存在,JDK1.7的做法就是将堆内存中"str01"的引用复制到了常量池中;
③执行时会在常量池中创建一个"str01"对象,发现已经存在,因而不会新建;
那么此时的str和str1都将指向的是堆内存中的"str01"的值,所以两者相等;