Java内存区域
在学习java虚拟机的过程中,发现jvm的许多设计其实和操作系统差不多,所以在学些的时候,可以类比操作系统来学习。
根据《java虚拟机规范》,Java虚拟机所管理的内存包括以下几个数据区域。
下面挨个介绍一下
程序计数器
是线程私有的
程序计数器,,类似计组里面的PC吧,是一块比较小的内存空间,可以看作字节码的行号指示器。 在虚拟机的概念模型里,字节码解释器工作时时通过改变这个计数器来取得下一条要执行的字节码指令(不一定是顺序执行。)
Java虚拟机栈
线程私有的
虚拟机栈的生命周期和线程的生命周期是相同的,描述的是Java方法执行时的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口。
每一个方法从调用到执行完成的过程,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
常把Java内存去分为堆内存和栈内存,其中栈内存就是指这个虚拟机栈,又或者说虚拟机栈中的局部变量表部分。
局部变量表存放了在编译器可知的各种数据类型(八大基本类型)(boolean,byte,char,short,int,float,long,double),对象引用类型,以及returnAddress类型(指向一条字节码指令的地址)
其中64位长度的long和double会占用两个局部变量空间,其它则占用一个。
在方法的运行期间,不会改变局部变量表的大小,因为在编译期,已经确定了栈帧所需的局部变量空间的打消了。
在虚拟机规范中,有以下两种一场。
- 请求栈深度 > 虚拟机所允许的深度,抛出 StackOverflowError
- 无法扩展至足够的内存 抛出 OutOfMemoryError
本地方法栈
主要用于Native方法,也有虚拟机将虚拟机栈和本地方法栈合二为一。
何为本地方法栈
- 是一种与本地有关,移植性较低的方法
- 主要用于加载文件和动态链接库。
因为Java无法直接访问Os底层信息,所以需要借助其他可以解除底层信息的语言,如 C
过程如下:
- java在类中声明一个Native方法,但是不实现
- 用javah命令编译层.h文件
- 使用C/C++重写,生成C/CPP文件,最后编译层.dll文件(动态链接库)
- 之后java使用Sytem类或Runtime类中的loadLibrary方法调用dll文件
Java堆
是所有线程公有的
Java堆(Heap)是虚拟机所管理的最大的一块内存空间。在虚拟机启动时创建。是垃圾收集器管理的主要区域,所有也被称为GC堆。
唯一目的是存放对象实例,几乎所有的对象实例都在这分配内存。
现在收集器基本采用分代收集算法,所以Java堆还可以分为新生代和老年代。
方法区
是所有线程共有的
用于存储已被虚拟加载的类信息,常量,静态变量,即编译器编译后的代码等数据。 别名为Non-Heap
在HotSpot虚拟机中,被称为永久代(Permanent Generation),但是其它虚拟机中不存在永生带这个概念。
这个区域的主要回收目标为常量池和对类型的卸载。
运行时常量池
为方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息为 常量池,用于存放编译器生成的各种字面量和富豪引用。
Java虚拟机对Class的每一部分都有严格规定。但对于运行时常量池,虚拟机规范没有做任何细节的要求。
运行吃常量池的另外一个重要特征是 具有 动态性。 常量不一定在编译期才会产生,运行期间也可能产生。
直接内存
指的是 系统的内存。