![](https://img.haomeiwen.com/i2897862/88bbd3a16a896095.png)
总体概览
Java虚拟机在运行java程序的时候会把它所管理的内存分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机的启动而一直存在,有些区域则依赖于用户线程的启动和结束而建立和销毁。
程序计数器
程序计数器是每个线程私有的空间,它保存了当前执行的字节码的行号。如果当前线程没有执行java方法而是在执行本地方法(native method)则程序计数器的值为空(Undefined)。
Java虚拟机栈
线程私有,生命周期与线程相同。每个方法被执行的时候,java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接和方法出口等信息。每个方法被调用直到执行完成的过程对应于栈帧入栈和出站的过程。
局部变量表存放了编译期间可知的各种java虚拟机基本数据类型、对象类型和returnAddress类型。这些数据类型在局部变量表中以局部变量槽来表示,长度为64位的数据类型占两个槽,其余类型占一个槽。局部变量表所需的大小(槽的多少)在编译时期就已经确定,运行时不会改变大小。
本地方法栈
与java虚拟机栈所发挥的作用是相似的,只不过java虚拟机栈用于执行java方法,而本地方法栈用于执行本地方法(native method)。
Java堆
java堆是java虚拟机管理的内存中最大的一块,它被所有线程共享。java虚拟机规范规定所有的对象实例以及数组都应当分配在堆上。Java堆是垃圾收集器管理的区域,从回收内存的角度看,由于现代垃圾收集器大都基于分代收集的理论设计的。所以Java堆中经常会出现“新生代”、“老年代”、“永久代”、“Eden空间”、“From Survivor空间”和“To Survivor空间”等名词。这些区域的划分仅仅是一些垃圾收集器共有的设计风格而已,并非java虚拟机规范的规定。
从分配内存的角度看,java堆中可以划分出多个线程私有的分配缓冲区,以提升对象分配时的效率。
方法区
方法区与堆一样是各个线程共享的区域,用于存储已被虚拟机加载的类型信息、常量、静态变量和即使编译器编译后的代码缓存。java虚拟机规范中把方法区描述为堆的一个逻辑部分,但为了将它与真正的堆区分开,开发人员给它起了一个别名---非堆(Non-Heap)。
JDK8以前的HotSpot虚拟机使用永久代来实现呃方法区,这样使得垃圾收集器能够像管理java堆一样来管理方法区,从而避免了为方法区专门编写垃圾收集算法。但由于永久代的大小有上限限制,因此使用永久代来实现方法区可能会造成内存溢出问题。JDK8及以后的HotSpot虚拟机已经放弃了使用永久代实现方法区的做法,而改用本地内存中实现的元数据(meta-space)来实现。
运行时常量池
运行时常量池是方法区的一部分,负责保存和管理Class文件中的常量池部分。但相对于Class文件中的常量池,运行时常量池并不要求常量一定只在编译期才能产生,运行期间也可以把新的常量放到运行时常量池中,这种特性的体现就是String
的intern()
方法。
总结
![](https://img.haomeiwen.com/i2897862/827a70319a88e2d0.png)
- java堆:多线程共享,垃圾收集,线程私有分配缓存
- java栈:线程私有,每个方法对应一帧,包括局部变量表、操作数栈、方法出口和动态链接
- 程序计数器:线程私有,当前java方法字节码的行号,native方法时为undefined
- 方法区:线程共有,存放类型信息和运行时常量池
- 运行时常量池:方法区的一部分,存放Class文件中的常量池信息,可运行时动态添加常量
网友评论