java虚拟机在运行过程中会将其所管理的内存划分为多个区域,根据各自用途可以大体划分为以下四个区域:堆,栈,方法区,程序计数器,其中栈又可分为本地方法栈和虚拟机栈,下图可以直观的显示内存中各个区域的划分情况。
程序计数器
程序计数器在内存中只占了很小一部分,用来指向下一条指令的地址,字节码解释器在执行过程中根据程序计数器的值选取执行的指令。
java通过在不同线程之间切换来执行多线程任务,同一个处理器同一时刻只能处理一个线程,为了保证切换之后能获取之前的执行状态,因此每个线程都拥有一个单独的程序计数器,多个线程之间的计数器互不影响。
若当前线程执行的是java方法,程序计数器的值为所执行指令的地址;若执行的native方法(非java代码),则值为undefined。
此区域是唯一一个不存在OutOfMemoryError的区域。
堆(heap)
堆是虚拟机管理的内存中最大的一块,在虚拟机启动时即被创建,所有线程共享该区域。堆上存放对象的实例和数组。主流虚拟机堆内存的大小都是可调节的(通过-Xms和-Xmx控制),若堆中没有内存可供实例分配,将抛出OutOfMemoryErroe错误。
堆是垃圾管理器管理的主要区域,由于现在的GC基本采用分代收集算法,因此堆还可以分为:新生代和老年代,新生代又可细分为Eden,From Survivor,To Survivor。(见参考文章 1)
方法区
方法区又叫静态区域,与堆一样,所有线程共享该区域。方法区存放被虚拟机加载的类的信息,常量,静态变量,编译后的代码,当方法区无法满足内存分配时,将抛出OutOfMemoryErroe错误。
运行时常量池
运行时常量池是方法区的一部分,受方法区内存限制,内存不足时将会抛出OutOfMemoryErroe错误。要理解运行时常量池需要理解以下三个概念:
- 常量池(Constant Pool):常量池数据编译期被确定,是Class文件中的一部分。常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。
- 字面量:文本字符串、声明为final的常量值等。
- 符号引用:类和接口的完全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符。
- 字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。
- 运行时常量池(Runtime Constant Pool):方法区的一部分,所有线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。
java虚拟机栈
和程序计数器一样,虚拟机栈也是线程私有的,生命周期跟线程相同。虚拟机栈表示Java方法执行的内存模型,每调用一个方法就会为每个方法生成一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口等信息(见参考资料4)。每个方法被调用和完成的过程,都对应一个栈帧从虚拟机栈上入栈和出栈的过程。
本地方法栈
作用和虚拟机栈类似,虚拟机栈执行java方法,本地方法栈执行native方法(非java代码)。