一、运行时数据区域
线程隔离的数据区:虚拟机栈、本地方法栈、程序计数器。
所有线程共享的数据区:方法区、堆。
二、
2.1程序计数器
程序计数器是一块较小的内存空间,用来指示当前线程执行的字节码的行号,完成分支,循环,跳转的功能。
为什么会需要程序计数器作为线程隔离的内存区域而不是所有线程共用?因为Java虚拟机的多线程是通过切换时间片的方式来实现的,因此,在一个确定的时刻,一个处理器只会执行一条线程。因此,为了线程切换之后仍能够回到正确的字节码行号,必需每个线程有一个独立的程序计数器。
2.2Java虚拟机栈
Java虚拟机栈是线程私有的,它的生命周期与线程相。虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧来存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直到完成的过程,就对应于一个栈帧在虚拟机栈中入栈到出栈的过程。
与内存分配最密切的就是虚拟机栈中的局部变量表和堆这两部分。局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型,returnAddress类型指向了一条指令字节码的地址。
其中,64为长度的long和double类型的数据会占用2个局部变量空间,其余的数据类型只占用一个。局部变量表所需要的空间在编译期间完成分配。
如果线程所请求的栈深度超过了java虚拟机栈所允许的深度。将抛出StackOverflowError异常。如果虚拟机栈可以动态扩展,但是扩展时无法申请到足够的内存,就会抛出OutOfMemorryError异常。
2.3本地方法栈
与虚拟机栈十分相似,只是虚拟机栈为虚拟机执行java方法,而本地方发展则为虚拟机所使用到的Native方法服务。与虚拟机栈一样,本地方法栈区域也会抛出StackOverFlowError和OutOfMemorryError异常。
2.4Java堆
Java堆是被所有线程共享的内存区域,在虚拟机启动时创建。几乎所有的对象实例都在这里分配。Java堆是垃圾收集器管理的主要区域。Java的大小可以通过-Xmx和-Xms控制。如果堆无法再扩展时,将会抛出OutOfMemorryError异常。
2.5方法区
方法区用于存储已被虚拟机加载的类信息,常量,静态变量等数据。这区域的内存回收目标主要在于对常量池的回收和对类型的卸载。
其中,运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用。
2.6直接内存
NIO通过基于通道与缓冲区的I/O方式,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存进行操作。这样能显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
栈上的reference数据被用来操作堆上的具体对象,可以通过这个reference引用来定位、访问堆中的对象的具体位置。目前的主流访问方式有两种:使用句柄和直接指针。
Java堆的最小值设置参数-Xms,最大值设置参数-Xmx。两种设置为一样可以避免堆自动扩展。
Java堆内存的OOM异常是实际应用中常见的内存溢出异常情况。出现OOM时,应该分清是内存泄露还是内存溢出。‘