1. java内存区域
jvm在执行java程序时会把他管理的内存区域划分为不同的数据区域,这些区域存储个java程序的数据,有些数据随着jvm启动而一致存在,有些数据随着java线程启动和结束而产生和清理。
2. jvm中的内存划分
线程私有表示每个线程中都会分配这样的内存区域,由线程共享表示所有线程共享访问同一块内存,若不进行线程控制,会出现线程不安全问题;
3. 程序计数寄存器(Program Counter Register)
Java虚拟机支持一次执行多个线程(一段时间,多线程并发执行,在同一时间点,一个处理机的内核只会执行一条线程中的指令)。每个线程中都有pc,若当前执行的代码属于非native方法,pc指向当前正在执行的指令地址,若当前执行的代码属于native方法,pc指向是undefined的。这里的程序计数程序寄存器和我们计算机组成,汇编中学到pc寄存器功能作用差不多。当然具体实现pc功能的时候,pc可能是一个数据结构(含多个字段),总之,pc需要承担推动线程中字节码的解释工作,程序往下运行。
3. 虚拟机栈(VM Stack)
虚拟机栈也是线程私有的,虚拟机栈存储frame,当线程中一个方法调用是,frame就被创建,压入虚拟机栈,当方法执行结束,弹出虚拟机栈。值得注意的是,jvm虚拟机规范规定frame之间不一定需要内存是连续的,也可以是非连续的。(即VM Stack 数据结构既可以为链表组成栈,也可以是一大块内存连续的数组组成栈),且frame的内存可以是来自堆(Heap).总之,虚拟机栈需要承担方法的调用和返回。
java虚拟机规范规定,VM Stack 的大小即可以固定,也可以动态拓展。如果VM Stack具有固定的大小,则在创建每个Java虚拟机堆栈时,建议可以独立选择它们的大小,也可以由程序员手动控制VM Stack大小。
在HotSpot虚拟机可以使用-Xss参数设置VM Stack大小。
在VM Stack中有以下两种异常:
(1)如果线程中需要的虚拟机栈深度比允许的虚拟机栈深度还要大,会抛出StackOverflowError。
(2)如果虚拟机允许VM Stack自动拓展,但是物理机中已经没有额外内存给虚拟机申请,会抛出OutOfMemoryError.
4. 堆(Heap)
堆(又称为GC堆)是被java虚拟机所有线程共享的java内存区域。所有的类的实例和数组的内存都在这里开辟。(java虚拟机规范是这样描述的“The heap is the run-time data area from which memory for all class instances and arrays is allocated”)。 当虚拟机启动的,Heap就会被创建,Heap中的内存管理有垃圾回收器管理(Garbage Collection,简称GC),《java虚拟机规范》规定heap的大小可以是固定的,也可以是可拓展收缩的。若是可拓展的,程序员可以控制Heap的最大值和最小值。(HotSpot中可以通过 -Xmx -Xms设定最大最小Heap值)。总之,Heap主要是存放对象实例和数组的地方,他的内存空间的管理由GC控制。
Heap中有一种异常:
(1) 当java程序需要的heap内存大小大于jvm 中可以提供的Heap最大内存大小时,会抛出OutOfMemoryError。
5. 方法区(Method Area)
方法区也是被java虚拟机所有线程所有线程共享的java内存区域。Method Area 类型于编程语言的编译代码的存储区域或类似于操作系统中进程的“text”段(segment)。它存储每个类(注意不是类的实例)的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法。(special method)。
方法区也是造JVM启动时创建,虽然方法区在逻辑上属于Heap的一部分(对于逻辑上的个人理解:《java虚拟机规范》没有规定Method Area的位置,也没有规定缓存的编译代码的处理策略),但是可以在方法区上不做垃圾收集或压缩它。方法区的大小也是即可以固定大小,也可以根据计算的需要扩展,且方法区的java内存也不需要要求连续。
方法区中有一种异常:
(1) 当方法区的内存不满足请求的内存分配,JVM会抛出OutOfMemoryError。
5.1 运行时常量池(Run-Time Constant Pool)
运行时常量池是方法区的一部分。运行时常量池是class文件中每一个类或者每一个接口中的常量池表(constant_pool table)的表现形式。它包含多种常量,包括编译产生的各种字面量和运行时解析的方法、字段引用。
运行时常量池中有一种异常:
(1) 当运行时常量池所需要的内存比方法区可用的内存还要多时,JVM会抛出OutOfMemoryError。
6. 本地方法栈(Native Method Stack)
Java虚拟机的实现可以使用常规的堆栈(俗称“ C堆栈”)来支持本机方法(本地方法指的是用Java编程语言以外的语言编写的方法)。本地方法栈或许被用在采用c语言来编写Java虚拟机的指令集的解释器场景下。Java虚拟机实现不需要加载本地方法,且自身不依赖于本地方法,故在JVM 内存中一定分配本地方法栈内存。如HotSpot虚拟机就将虚拟机栈和本地方法栈合二为一,如果JVM内存中存在Native Method Stack,则需要为每个线程分配独自的本地方法栈,即线程私有。总之,VM Stack是为虚拟机执行java方法(字节码)服务,而本地方法栈是为了虚拟机使用Native方法服务。《java虚拟机规范》允许本地方法堆栈具有固定大小,或者根据计算要求进行动态扩展和收缩。
本地方法栈中有两种异常:
(1) 栈深度溢出,JVM会抛出StackOverFlowErrot。
(2) 创建本地方法栈时,可用的内存不够,JVM会抛出OutOfMemoryError。
参考文献:
[1]深入java虚拟机 周志明 第三版
[2]操作系统 汤子赢
[3]计算机组成原理 唐朔飞
[4]https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5(The Java Virtual Machine Specification, Java SE 8 Edition)