java内存区域
运行时数据区域
线程私有:程序计数器、本地方法栈、java虚拟机栈
线程共享:堆内存、方法区、直接内存(非运行时数据区的一部分)
1、程序计数器:
当前线程所执行的字节码的行号指示器。作用一:选取下一个需要执行的字节码指令,完成分支、循环、跳转、异常处理等功能。作用二:多线程情况下记录当前线程的执行位置,从而当线程切回时,知道这个线程运行到哪里。
2、java虚拟机栈:
每个栈帧存有:局部变量表部分(基本数据类型+refrence引用地址)、方法出口信息、操作数栈、动态链接。补充:调用一个方法时:会经历压栈和弹栈过程。
3、本地方法栈:
虚拟机栈为执行java方法(也就是字节码)服务,本地方法栈为虚拟机使用到的native方法服务。
4、堆:
唯一目的就是存放对象实例。几乎所有对象实例及数组都在这里分配内存。内存又分为:新生代内存、老生代内存、元空间(直接内存)。且运行时的常量池在堆区。
5、方法区:
存储已经被虚拟机加载的类信息、常量、静态变量、编译后的代码等数据。
6、直接内存:
避免在java堆和native堆之间来回复制数据。
对象的创建过程
类加载检查==> 分配内存 ==>初始化零值 ==> 设置对象头 ==> 执行init方法
Step1:类加载检查:
虚拟机收到new指令,在常量池中定位这个类的符号引用,判断这个类是否加载过、解析和初始化过,没有就先执行。
Step2:分配内存:
在堆中分配内存。
分配方式:指针碰撞、空闲列表。取决于java堆是否规整(有没有内存碎片),是否规整又取决于垃圾收集器是否有压缩整理功能。
Step3:初始化零值:
将分配到的内存空间都初始化为零值,保证对象的实例字段在java代码中不赋予初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。
Step4:设置对象头:
存储类的元数据(指向类)、对象的哈希码、对象的GC分代等。
Step5:执行init方法
没有执行init方法,所有字段都是零值,执行后才是按照程序员设置的初始值,对象才真正创建出来。
对象的访问定位
使用句柄方式:
堆中划分一块内存作为句柄池,在栈里存储的就是对象的句柄地址,每个对象的句柄地址包括:对象实例数据的指针、对象类型数据的指针。
好处:对象移动时,只需要改变句柄中的指针,而不需要改变栈里的句柄地址
直接指针方式:
栈里存储的就是对象的引用地址,对了实例数据里存储了对象类型数据的指针,指向对象类型。
好处:速度快,直接。