JVM内存区域介绍
学习JVM第一个要了解的就是JVM的内存区域。
Java虚拟机在运行时会从操作系统内存中划分一部分出来作为JVM内存,而JVM内存又划分为以下几个区域:
大体上可以分为两种:
线程共享数据区
该类型的数据区,多个线程共用一个数据区。
线程私有数据区
该类型的数据区,每个线程都拥有自己独立的数据区。
下面逐一介绍图中的数据区:
程序计数器(Program Counter Register)
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
字节码解释工作就是通过改变这个计数器的值来选取下一条要执行的字节码指令,分支、循环等基础功能需要通过这个计数器来完成。
Java的多线程是通过线程轮流切换并分配处理器执行时间来实现的,在任一个时刻,一个处理器(多核处理器的一个内核)只会执行一条线程中的指令。因此为了线程切换后能够恢复到正确的位置,每条线程都需要有一个独立的程序计数器,因此程序计数器是“线程私有数据区”。
虚拟机栈(JVM Stacks)
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用来存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(int、float...)、对象引用(reference类型,它不等于对象本身,只是一个指针或句柄等代表对象地址的信息)。
局部变量表所需的内存空间在编译期间完成分配。
本地方法栈(Native Method Stack)
本地方法栈与虚拟机栈类似,只是虚拟机栈为执行Java方法服务,而本地方法栈为Native方法服务。
在虚拟机规范中没有对本地方法栈做强制规定,因此虚拟机可以自由实现它。在HotSpot虚拟机中,直接将本地方法栈和虚拟机栈合二为一了。
堆(Java Heap)
堆是jvm管理的内存中最大的一块。堆是被所有线程所共享的一块区域,属于“线程共享数据区”,在jvm启动时创建。
此区唯一的目的就是存放对象实例,几乎所有的对象实例和数组都是在这里分配内存的。
堆是垃圾收集器管理的主要区域,因此也被称为“GC堆”。
堆可以处于物理上不连贯的内存空间内。
方法区(Method Area)
方法区和堆一样,是“线程共享数据区”,它用来存储已被jvm加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
jvm规范对方法区的限制非常宽松,除了和堆一样不需要连续的物理内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。
方法区包含运行时常量池。运行时常量池用来存放编译期生成的各种字面量和符号引用。
补充
jdk1.4中引入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配jvm内存外的操作系统内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在java堆和native堆中来回复制数据。