Java运行时数据区域
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域。其中深色区域为线程共享,而浅色区域为线程私有。
1 程序计数器
程序计数器是一块比较小的内存空间。它可以看作是当前线程所执行的字节码的行好指示器。在虚拟机的概念模型里,字节码的解释器工作就是改变这个计数器的值来选取下一条字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
Java中多线程是通过线程轮流切换并分配处理器的执行时间的方式来实现的,在一个时刻,一个处理器只会执行一个线程中的指令。因此为了线程切换后恢复到正确的执行位置,每个线程都需要一个独立的程序计数器。因此,程序计数器是线程私有的内存。
如果线程正在执行Java方法,则计数器中的内容是字节码指令的地址,如果是Native方法,则计数器值为空。该内存区域是Java虚拟机规范中没有规定任何OutofMemoryException的区域。
2 Java虚拟机栈
它是线程私有的,生命周期与线程相同。
每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完的过程都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放方法参数和方法内部定义的局部变量,这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和 double类型的数据会占用两个变量槽,其余的数据类型只占用一个。局部变量表所需的内存空间在编 译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定 的,在方法运行期间不会改变局部变量表的大小。这里说的“大小”是指变量槽的数量, 虚拟机真正使用多大的内存空间(譬如按照1个变量槽占用32个比特、64个比特,或者更多)来实现一 个变量槽,这是完全由具体的虚拟机实现自行决定的事情
异常:如果线程请求的栈深度大于虚拟机所允许的深度,则会跑出StackOverflowError异常。如果线程扩展时,无法申请到足够的内存,就会抛出OutofMemoryError异常。
3 本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机 栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native) 方法服务。本地方法栈也会在栈深度溢出或者栈扩展失 败时分别抛出StackOverflowError和OutOfMemoryError异常。
4 Java堆
绝大多数情况下,Java堆是Java虚拟机锁管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此区域的目的是存放对象实例和数组。
Java堆是垃圾回收的主要区域,由于垃圾收集基本采用分代算法,所Jjava堆中还可以细分为:新生代和老年代。根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间,只要逻辑上是连续的即可。如果堆中没有内存来分配实例,并且堆也无法扩展时,将会抛出OutofMemoryError异常。
-Xms: 指定虚拟机堆内存初始值大小
-Xmx: 指定虚拟机堆内存最大值大小
5 方法区
方法区是线程共享区域,它用于存储已被加载的类信息、常量、静态常量、即时编译后的代码等数据。它有一个别名叫Non-Heap这个区域的内存回收只要针对常量池和类型的卸载。
运行时常量池
运行时常量池时方法区的一部分。Class文件中除了有类的版本、字段方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。
在 JDK 1.8 中, HotSpot 已经没有 “PermGen space”这个区间了,取而代之是一个叫做 Metaspace(元空间) 的东西。下面我们就来看看 Metaspace 与 PermGen space 的区别。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此默认情况下,元空间的大小仅受本地内存限制
JDK 8 中永久代向元空间的转换。有一个疑问,就是为什么要做这个转换?所以总结以下几点原因:
1. 字符串存在永久代中,容易出现性能问题和内存溢出。
2. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
6 直接内存
直接内存并不是虚拟机运行时数据区的一部分,JDK1.4引入NIO类,是一种基于通道与缓冲区的IO方式,他可以使用Native函数直接分配堆外内存,然后通过堆中的DirectByteBuffer对象作为引用来操作这块内存。这块内存不归JVM垃圾收集器管,但也是需要回收释放。具体请移步至https://blog.csdn.net/TheLudlows/article/details/101061407