Java运行时的数据区域
Java程序执行过程中,Java虚拟机会把它的内存划分成好几个不同的区域,有些区域是随着虚拟机的启动就生成了而有些区域是随着用户的线程创建而生成,随用户线程得结束而销毁。
Java运行时数据区域-
程序计数器
主要用来作为当前线程所执行的字节码的行号指示器,就是通过它来知道需要执行的字节码指令地址。这块区域是线程私有的,你看如果是线程共享的那切换线程之后谁知道下一条执行该执行哪里。如果执行的Native方法的话,计数器的值就不是需要执行的字节码指令地址了,而是Undefined。
在内存区域中计数器是唯一一个没有规定OutOfMemoryError情况的区域。
-
虚拟机栈
虚拟机栈是线程私有的,也就是每个线程它自身有一个虚拟机栈,栈里面放的是一个一个栈帧,每一个栈帧对应着一个方法的调用,也就是一个方法的调用就是一个栈帧的入栈,方法执行结束就是一个栈帧的出栈。
栈帧主要用来存储局部变量、操作数栈、动态链接、方法出口等信息。所以多线程操作方法内部的局部变量就不需要担心出什么可见性或者原子性问题了,因为它是线程私有的!
其中的局部变量表存放的是编译期就已知的各种基本数据类型、对象的引用和returnAddress类型(为字节码指令jsr、jsr_w和wet服务的,它指向了一条字节码指令的地址)。
除了long和double占用两个局部变量空间(slot),其他数据类型都只占用1个空间,其所需的内存空间在编译器已确定。
当线程请求的栈深度大于虚拟机所允许的深度(例如递归深度太深了),将抛出StackOverflowError。或者如果虚拟机栈允许动态扩展那当扩展到无法申请需要的内存时候则抛出OutOfMemoryError。
-
本地方法栈
和虚拟机栈很相似,主要区别就在于它是服务于Native方法,虚拟机栈服务于Java方法。有些虚拟机把本地方法栈和虚拟机栈合二为一例如(HotSpot)虚拟机。并且和虚拟机栈一样会抛出StackOverflowError和OutOfMemoryError。
-
堆
绝大部分情况下,堆是这几部分中所占内存最大的一块。它是所有线程共享的内存区域,随着虚拟机的启动而创建,它的目的就是存放对象实例!所有的对象实例以及数组都要在堆上面分配。
并且堆也是垃圾收集器的主要管理区域。根据垃圾处理器的分代收集算法,在堆中分为新生代和老年代。当内存不足的时候将抛出OutOfMemoryError。
-
方法区
和堆一样,是所有线程共享的内存区域,用来存放已经被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等。编译器时就将各种生成的字面量和符合引用放入常量池,在运行期间也有可能有新的常量加入例如用了String的intern()方法。
根据垃圾处理器的分代收集算法,永久代就在这里。当内存不足的时候将抛出OutOfMemoryError。
-
直接内存
直接内存不是虚拟机运行时的数据区域的一部分,但是这里还是来说说。因为它还是很频繁的被使用的!
NIO(New Input/Output),是通过通道和缓冲区的I/O方式,它使用Native函数库直接分配堆外内存,然后通过堆中的DirectByteBuffer对象作为引用来操作这块内存。
正常的内存流应该是本地IO-->直接内存-->非直接内存-->直接内存-->本地IO。
而直接内存就是本地IO-->直接内存-->本地IO。
所以它能在一些通用的场景显著的提高性能。直接内存的大小受本机的总内存限制!