Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。 这些区域有各自的用途, 以及创建和销毁的时间, 有的区域随着虚拟机进程的启动而一直存在, 有些区域则是依赖用户线程的启动和结束而建立和销毁。
线程私有内存,生命周期与线程相同。它可以看作是当前线程所执行的字节码的行号指示器,记录的是正在执行的虚拟机字节码指令的地址。它是唯一一个不会出现OutOfMemoryError 的区域。
线程私有内存,生命周期与线程相同。在每个方法执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。口头中的栈通常指的是虚拟机栈中的局部变量表。
局部变量表中存放了编译期间可知的基本数据类型(boolean、 byte、 char、 short、 int、float、 long、 double)、对象引用(reference类型) 和returnAddress类型(指向了一条字节码指令的地址)。 这些数据类型在存储空间中以局部变量槽(Slot)表示,64位的long和double占两个变量槽,其余的占一个。局部变量表在栈帧中分配的局部变量空间大小是在编译期间确定的,在方法运行期间是不会改变的局部变量表的大小的。 Slot是可以重用的,当Slot中的变量超出了作用域,那么下一次分配Slot的时候,将会覆盖原来的数据。系统不会为局部变量赋予初始值(实例变量和类变量都会被赋予初始值)。也就是说不存在类变量那样的准备阶段。
操作数栈也常被称为操作栈, 虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈
虚拟机栈存在两类异常状况:
HotSpot虚拟机是不允许栈容量动态扩展的,如果申请成功了就不会报OutOfMemoryError异常,如果申请时就失败了,仍会出现OutOfMemoryError异常。
虚拟机栈容量设置-Xss参数,由于HotSpot虚拟机不区分虚拟机栈和本地方法栈,因此调整本地方法栈容量的参数是失效的
本地方法栈(Native Method Stacks) 与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码) 服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
与虚拟机栈一样, 本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。
Java堆是虚拟机所管理的内存中最大的一块,线程共享,此内存区域所存放的是对象的实例,几乎所有的对象实例都在这里分配内存。
Java堆既可以被实现成固定大小的, 也可以是可扩展的, 不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定) 。
如果在Java堆中没有内存完成实例分配, 并且堆也无法再扩展时, Java虚拟机将会抛出OutOfMemoryError异常。
Java堆的大小参数设置,堆最小值-Xms参数,堆最大值-Xmx参数
线程共享内存区域,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 。这块区域在JDK8以后抛弃了永生代的概念,改用元空间,这部分的垃圾收集主要是针对常量池的回收和对类型的卸载。
如果方法区无法满足新的内存分配需求时, 将抛出OutOfMemoryError异常
运行时常量池(Runtime Constant Pool) 是方法区的一部分。 Class文件中除了有类的版本、 字段、 方法、 接口等描述信息外, 还有一项信息是常量池表(Constant Pool Table) , 用于存放编译期生成的各种字面量与符号引用, 这部分内容将在类加载后存放到方法区的运行时常量池中。
运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性, Java语言并不要求常量一定只有编译期才能产生, 也就是说, 并非预置入Class文件中常量池的内容才能进入方法区运行时常量池, 运行期间也可以将新的常量放入池中, 这种特性被开发人员利用得比较多的便是String类的intern()方法。
在JDK 1.4中新加入了NIO(New Input/Output) 类, 引入了一种基于通道(Channel) 与缓冲区(Buffer) 的I/O方式, 它可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。 这样能在一些场景中显著提高性能, 因为避免了在Java堆和Native堆中来回复制数据。
本机直接内存的分配不会受到Java堆大小的限制, 但是, 既然是内存, 则肯定还是会受到本机总内存的限制, 当各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制), 从而导致动态扩展时出现OutOfMemoryError异常。
直接内存的容量大小可以通过-XX:MaxDirectMemorySize参数来指定,如果不指定,默认与Java堆最大值一样大
可能出现StackOverflowError异常的内存区域为Java虚拟机栈和本地方法栈
可能出现OutOfMemoryError异常的内存区域为Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存。