JVM系列之详解运行时数据区域

运行时数据区

1.程序计数器

是一块较小的内存空间,可以看作当前线程所执行字节码行号指示器。

下一条执行的字节码指令,就是根据改变计数器的值来选取,程序控制流的指示器,分支、循环、跳转、异常处理、线程回复等功能功能都需要依赖这个计数器完成。

每个线程都有自己独立的计数器,互不影响,独立存储。

2.Java虚拟机栈

每个方法执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被调用直至执行完毕的过程,就对应着一个栈桢在虚拟机中从入栈到出栈的过程。

通常所说的JVM里面的堆和栈里所指的栈就是Java虚拟机栈,或者说是Java虚拟机栈中局部变量表部分。

局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、short、int、long、float、double、char)、对象引用(reference类型,他并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和return Address类型(指向了一条字节码指令地址)。

局部变量表中的存储空间以局部变量槽来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个,局部变量表所需的空间在编译期间完成分配。

操作数栈则存储方法内一些进行了运算操作后的结果。

动态链接,在方法内调用接口,通过字面量链接到具体的实现类,实现Java的动态特性。

方法出口(返回地址),return或者发生Exception等。

3.本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法服务(也就是字节码)服务,而本地方法栈则是为虚拟机使用到本地(Native)方法服务。

4.Java堆

Java堆是被所有线程共享的一块内存区域,用于存放对象实例,Java世界里”几乎“所有的对象实例都在这里分配内存。

Java堆是垃圾收集器管理的内存区域,一些资料也被称作"GC堆”。由于现代垃圾收集器大部分基于分代收集理论设计,所以Java堆中经常会出现“新生代”“永久代”“Eden空间""From Survivor空间”“To Survior空间”等名词。

从内存的角度看,所有线程共享得Java堆中可以划分出多个线程私有得分配缓冲区(Thread Local Allocation Buffer,TLAB),以提升对象分配时的效率。将Java堆细分的目的只是为了更好地回收内存,或者更快的分配内存。

5.方法区

方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

说到方法区,就可以了解一下“永久代”这个概念,尤其在JDK8以前,许多Java程序员都习惯在HopSpot虚拟机上开发、部署程序,很多人都愿意把方法区称呼为“永久代”(Permanent Generation),或将两者混为一谈,本质上是不等价的。在JDK7的HopSopt,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了JDK8,终于完全放弃了永久代的概念,改用JRockit、J9一样在本地内存中实现的原空间来代替,把JDK7中永久代还剩余的内容(主要是类型信息)全部移动到原空间中。

经验分享 程序员 微信小程序 职场和发展