运行时数据区由 程序计数器、java虚拟机栈、本地方法栈、堆、方法区 组成;java
每个Java线程都有一个程序计数器,用于保存程序执行到当前方法的哪个指令,它是线程私有的。函数
此内存区域是惟一一个在VM Spec中没有规定任何OutOfMemoryError状况的区域。spa
一般说的栈指的就是Java栈,主管Java程序的运行。栈是在线程建立时建立,线程结束栈内存就释放掉了,不存在垃圾回收问题,线程一结束该栈就Over,与程序计数器同样,它的生命周期也是与线程相同,它是线程私有的。线程
基本类型的变量、实例方法、引用类型变量都是在函数的栈内存中分配。3d
栈描述的是Java方法调用的内存模型:每一个方法被执行的时候,都会同时建立一个帧(Frame)用于存储本地变量表、操做栈、动态连接、方法出入口等信息。每个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。本地变量表存放了编译期可知的各类标量类型(boolean、byte、char、short、int、float、long、double)、对象引用(不是对象自己,仅仅是一个引用指针)、方法返回地址等指针
这个区域规定了2种异常:若是线程请求的栈深度大于虚拟机所容许的深度,将抛出StackOverflowError异常;若是VM栈能够动态扩展(VM Spec中容许固定长度的VM栈),当扩展时没法申请到足够内存则抛出OutOfMemoryError异常。code
(1)抛出StackOverflowError异常的代码(递归调用):对象
public class StackDemo1 { static void sayHello() { System.out.println("AAAAA"); sayHello(); } public static void main(String[] args) { sayHello(); } }
本地方法栈和Java虚拟机栈发挥的做用是相似的,只不过Java虚拟机栈为虚拟机运行原语服务,而本地方法栈是为虚拟机使用到的 native 服务。blog
它的实现语言、结构、方式没有强制规定,甚至有的虚拟机把它和java虚拟机栈合二为一了,例如Sun Hotspot。递归
和java虚拟机栈同样,这个区域也会抛出StackOverflowError异常和OutOfMemoryError异常。
Java7以前:
一个JVM实例只存在于一个堆内存中,堆内存的大小是能够调节的。类加载器读取了类文件以后,须要把类、方法、常变量放到堆内存中,
保存全部引用类型的真实信息,以方便执行器执行。
堆内存逻辑上分为:新生区、养老区、永久区。(实际上永久区被称为非堆内存)
新生区:伊甸区(Eden Space)、幸存0区(Survivor 0 Space)、幸存1区(Survivor 1 Space)
当new 一个对象,该对象被放入伊甸区(Eden),建立的对象愈来愈多,伊甸区(Eden)快满的时候启动一种轻垃圾回收(Minor GC),未被回收的对象被放入幸存0区(Survivor 0),Eden被清空;当幸存0区快满了,未被回收的对象被放入幸存1区(Survivor 1),Survivor 0和Eden被清空;Survisor 0与Survivor 1交换角色,如此循环往复。若是对象的复制次数达到16次,该对象就会被送到养老中。当养老区快满的时候触发一个重量级的GC(Major GC),清理以后仍是没法再保存对象,就会产生OOM异常(OutOfMemoryError)。
Survisor 0 和 Survivor 1会一直调换角色,谁是空的谁就是Survivor 1区。
方法区是全部线程共享的,一般用来储存装载的类的元结构信息。垃圾回收不多发生;
好比:运行时常量池 + 静态变量 + 常量 + 字段 + 方法字节码 + 在类/实例/接口初始化用到的特殊方法等。
一般和永久区关联在一块儿(Java7),具体的跟JVM的实现和版本有关。Java8之后,变为了MetaSpace(元空间),直接使用的物理内存,垃圾回收运行的几率变得更低。