运行时数据区域
运行时数据区可以划分为两部分:线程隔离和线程共享的数据区。
线程隔离的包括:程序计数器,虚拟机栈和本地方法栈
-
程序计数器:可以看做是当前线程正在执行字节码的行号指示器。
线程隔离的原因是因为,JVM的多线程是通过多线程轮流切换,处理器分配时间完成的,在任何一个时刻,处理器只会处理一条线程的一条指令。线程切换之后依旧可以恢复到正确的指令位置就需要每个线程都有程序计数器,故线程隔离。
执行java方法,存放的是正在执行的字节码的地址;
执行本地方法,计数器为空。 -
虚拟机栈:java方法执行的线程内存模型:每个方法在执行的过程中,JVM都创建一个栈帧(Stack Frame)用以存储:局部变量表、操作数栈、动态连接、方法出口等信息。
局部变量表:基本数据类型、对象引用(如:指向对象起始地址的引用指针)和returnAddress类型(字节码指令)。
线程请求的栈深度如果大于虚拟机所允许的深度—>stackoverflowerror;
若可动态扩展栈深,当栈扩展无法申请到足够的内存—>outoffmemoryerror. -
本地方法栈:和虚拟机栈类似,只不过它是服务与本地方法。
线程共享的包括:java堆,方法区。
- java堆:被所有线程共享的一块内存区域,存放对象实例以及数组。从分配内存的角度来看,可以划分为多个线程私有的分配缓冲区,以提升效率; 物理上可以不连续,但逻辑上应该连续;堆的大小,可以固定也可以扩展(通过参数-Xmx,Xms设定)。
- 方法区:用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译之后的代码缓存。JDK8之后完全废除了“永久代”的概念,采用本地内存实现的元空间代替;不需要连续的内存,可以固定也可以扩展;这个区域的内存回收目标主要是:常量池的回收和类型的卸载
运行时常量池:方法区的一部分,在class文件中存放了常量池表用以存储编译期生成的字面量与引用符号,在类加载之后会被放入运行时常量池。运行期常量池相对于class文件常量池的另一个重要特征是具备动态性,Java并不要求常量一定在编译期生成,所以并不是必须进入class文件常量池才能进入运行时常量池,运行期也可以将常量放入池中。