目录
1 程序计数器
占用较小的内存空间,是当前线程执行的字节码的行号指示器,解释器就是通过这个来决定下一条执行的指令。程序计数器是线程私有的。
因为程序有可能以多线程运行,为了保证程序正确执行,每个线程必须持有一个自己的程序计数器,因此程序计数器是线程私有内存。
如果执行的是Java代码,计数器正常记录;如果是Native方法,计数器的值为 null。
这是Java虚拟机规范中唯一没有规定 OutOfMemoryError 的区域。
2 Java虚拟机栈
虚拟机栈是线程私有,生命周期与线程一样。
虚拟机栈描述的是 Java 方法(method )执行的内存模型:每个方法在运行的时候会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
我们经常讲的 堆内存、栈内存 当中的 栈内存 指的就是虚拟机栈,更准确一点说,就是虚拟机栈中的局部变量表。
虚拟机栈两种异常:StackOverflowError, OutOfMemeryError。
栈帧:是虚拟机运行时数据虚拟机栈的栈元素。每一个方法从调用开始到执行完成的过程就是一个栈帧压栈和出栈的过程。一个栈帧就可以理解成是一个方法。
当前栈帧:一个线程中的方法调用链可能很长, 方法 A 调用方法 B,方法 B 中又调用方法 C,但是对于执行引擎,在活动线程中,只有位于栈顶的栈帧才是有效的,称之为当前栈帧,与当前栈帧相关的方法称为当前方法。
当前栈帧的4个组成部分:局部变量表、操作数栈、动态链接、方法出口。
3 本地方法栈
本地方法栈与虚拟机方法栈的作用非常类似,只不过本地方法栈是为 Native 方法服务的,Native方法调用的是 C/ C++ 程序的方法。
有的虚拟机如 Sun HotSpot 虚拟机,将本地方法栈和虚拟机栈合二为一了。
异常:StackOverflowError, OutOfMemery。
4 Java堆
Java堆是垃圾回收的主要区域,所有的对象实例和数组都在堆上分配。
Java堆又分为如下几个区域:
-
新生代
-
Eden
-
From Survivor
-
To Survivoe
-
-
老年代
Java 堆 这样分区的的好处是,根据对象的存活时间,可以将不同的存活对象分配到不同的“代”当中去,结构清晰;另一个好处是,可以根据不同的 “代”,采用不同的垃圾回收机制。
例如,因为新生代中,很多对象(内存空间)都是短暂存在的,使用了之后马上就能被回收,所以新生代每回收一次,就能回收 60% - 70% 左右的内存空间,“性价比” 很高,所以新生代采取了叫做“复制算法”进行垃圾回收。
异常:OutOfMemeryError
5 方法区
必要说明
-
非堆:JVM 规范中,方法区是堆的一个逻辑部分,但是人们为了区分它跟堆,给它取了一个别名,叫“非堆”。
-
永久代:习惯在 HotSpot 旧版本虚拟机上开发、运行程序的程序开发人员也经常将方法区称为“永久代”,这是因为旧版本的 HotSpot 虚拟机将 GC 分代收集 拓展至了方法区,或者说是用“永久代”实现了方法区。
-
元空间:即使是 HotSpot 虚拟机,在 JDK1.8 及以后,也没有“永久代”了,而是用“元空间”取代了“永久代”。元空间直接利用物理机的内存,不占用 JVM内存。
方法区的作用
存储类的元数据信息如:类基本信息、常量、静态常量、即使编译器编译后的代码等数据。
方法区是线程共享的。
异常:OutOfMemeryError
6 总结
-
Java虚拟机运行时数据区域的5个组成部分:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区。其中,只有方法区和Java堆 是线程共享区域,其余3 个部分是线程独有区域。
-
方法区存储的是类的元数据信息、常量、静态变量等信息。
-
Java 程序中所有的实例对象、数组、实例变量都是存储在堆中的。
-
Java 虚拟机栈里面的内存空间存储的是局部变量表,我们经常说,局部变量表存储的是 8 种基本数据类型 和 引用类型变量,很多人会有疑惑:实例变量如果是基本类型,那么它是存储在虚拟机栈中的吗?答案是:不是。上面那句话的准确理解是:传递给实例方法的参数 和 在实例方法中定义的局部变量,如果它们(方法参数 + 方法中的局部变量)的类型是基本类型,那么它们直接存储在 虚拟机栈的局部变量表中,如果它们的类型是 引用类型,如
Person p = new Person("zhangsan")
,那么虚拟机栈的局部变量表存储的只是 p 这个引用变量,它指向 new 出来的这个 Person 对象,而 new 出来的这个 Person 对象实际存储在 Java 堆中。 -
虚拟机栈和本地方法栈,因为都是调用栈,所以都有 StackOverflowError ,所以使用递归的时候,要注意递归深度的问题。
-
除了 程序计数器,其它几个部分都由 OutOfMeneruError,程序计数器只是一个记录代码行数的计数器,所以占用的内存空间是固定的,且实在是太微不足道,所以不会有内存溢出的错误。