Java 虚拟机在执行 Java 程序的过程当中会把它所管理的内存划分为若干个不一样的数据区域,这些区域都有各自的用途,以及建立和销毁的时间。有的区域随着虚拟机进程的启动就存在了, 有的区域则是依赖用户线程。根据《Java虚拟机规范(第二版)》,Java 虚拟机所管理的内存包含如下的几个区域。java
由上图能够看出,在运行时数据区中:虚拟机栈、本地方法栈、程序计数器属于线程隔离的数据区,是单个线程私有的,它们的生命周期与线程相同;而方法区和堆属于全部线程共享的数据区,是全部线程共享的。算法
程序计数器(Program Counter Register)是最小的一块内存区域,它能够看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工做时就是经过改变这个计数器的值来选取吓一跳须要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都须要依赖这个计数器来完成。在多线程环境下,当某个线程失去处理器执行权时,须要记录该线程被切换出去时所执行的程序位置。从而方便该线程被切换回来(从新被处理器处理)时能恢复到当初的执行位置,所以每一个线程都须要有一个独立的程序计数器。各个线程的程序计数器互不影响,而且独立存储。数组
Java 虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同。Java 虚拟机栈描述的是 Java 方法执行的内存模型:每一个方法在执行的同时都会建立一个栈帧(Stack Frame)用于存储局部变量表、操做数栈、动态连接、方法出口等信息。每一个方法从调用直至执行完成的过程,对应着一个栈帧在虚拟机中入栈到进栈的过程。缓存
局部变量表存放了编译期克制的各类基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象自己,多是一个指向对象起始地址的引用指针,也多是指向一个表明对象的句柄或其它与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址)。其中 64 位长度的 long 和 double 类型的数据会占用 2 个局部变量表空间(Slot),其他的数据类型只占 1 个。局部变量所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法须要在帧中分配多大的局部变量空间是彻底肯定了的,在方法运行期间不会改变局部变量表的大小。数据结构
在 Java 虚拟机规范中,对这个区域规定了两种异常情况:多线程
本地方法栈(Native Method Stack)虚拟机栈所发挥的做用是很是类似的,它们的区别不过是虚拟机栈为虚拟机执行 Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并无强制规定,所以具体的虚拟机能够自由实现它。甚至有的虚拟机(譬如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈同样,本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。spa
也叫作java 堆(Java Heap)、GC 堆(Garbage Collected Heap)是 java 虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,在 JVM 启动时建立。该内存区域存放了对象实例及数组(全部 new 的对象)。其大小经过 -Xms(最小值)和 -Xmx(最大值)参数设置,-Xms 为 JVM启动时申请的最小内存,默认为操做系统物理内存的 1/64 但小于 1G,-Xmx 为 JVM 可申请的最大内存,默认为物理内存的 1/4 但小于 1G,默认当空余堆内存小于 40% 时,JVM 会增大 Heap 到 -Xmx指定的大小,可经过 -XX:MinHeapFreeRation= 来指定这个比列;当空余堆内存大于 70% 时,JVM 会减少 heap 的大小到 -Xms 指定的大小,可经过 XX:MaxHeapFreeRation= 来指定这个比列,对于运行系统,为避免在运行时频繁调整 Heap 的大小,一般 -Xms 与 -Xmx 的值设成同样。操作系统
Java 堆是垃圾收集器管理的主要区域,从内存回收的角度来看,因为如今收集器基本是采用分代收集算法,堆被划分为新生代和老年代。新生代主要存储新建立的对象和还没有进入老年代的对象。老年代存储通过屡次新生代 GC(Minor GC) 任然存活的对象。线程
方法区(Method Area)与 Java 堆同样,是各个线程共享的内存区域,它用于内存已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,可是它却有一个别名叫作 Non-Heap(非堆)。对于 HotSpot 虚拟机,也把方法区成为“永久代”(Permanent Generation),默认最小值为16MB,最大值为64MB,能够经过 -XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。在 JDK 1.7 之后已经逐步改成采用 Native Memory 来实现方法区。当方法区没法知足内存分配需求时,将抛出 OutOfMemoryError 异常。指针
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中处了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译器生成的各类符号引用,这部份内容将在类加载后放到方法区的运行时常量池中。当常量池没法再申请到内存时会抛出 OutOfMemoryError 异常。对于 HotSpot 虚拟机,在 JDK 1.7 中,已经把本来放在永久代的字符串常量池移除。
直接内存(Direct Memory)并非虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。可是这部份内存也被频繁地使用,并且也可能致使 OutOfMemoryError 异常。jdk1.4 中新加入的NIO,引入了通道与缓冲区的IO方式,它能够调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小。
欢迎扫一扫关注 程序猿pdh 公众号!