JVM内存模型
引言
JVM是运行在操作系统之上,而JVM是要想了解JVM虚拟机运行的内幕,必须要先知道其内存模型
根据JVM规范,JVM内存共分为五块区域
本文围绕这个几个区域,剖析JVM运行时数据区
JVM运行时数据区
1.程序计数器
程序计数器是线程私有的,也就是意味着,每一个线程都有自己的一个独立程序计数器,里面记载着当前线程执行下一条字节码指令的行号,它的生命周期跟其对应的线程生命周期一样。
这里的程序计数器指的是JVM运行时指的下一条字节码的行号,跟CPU当中的程序计数器不一样,可以这么理解,这里的程序计数器是JVM指令级别的,而CPU内部的程序计数器是位于CPU的一个物理空间,通常我们把它称作——寄存器,它指向的是下一条CPU指令的内存地址,它是CPU指令级别的。
特点
- 线程私有
- 占用内存很小
- 在内存当中不可能出现OOM(OutOfMemory),所有对一这块区域也不存在垃圾回收这一说法
- 如果是在执行native方法,则是未指定值(undefined),因为程序计数器不负责本地方法栈。
2.JVM虚拟机栈
虚拟机栈也是线程私有的,一个线程对应着虚拟机栈里面一块区域。线程运行过程中,每个方法在执行的时候会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError(递归调用)。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。
3.堆(Heap)
堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
在默认的情况下,堆中各区域的大小占比
老年代:2/3的堆空间
年轻代:1/3的堆空间
eden区:8/10 的年轻代
survivor0: 1/10 的年轻代
survivor1:1/10的年轻代
4.本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError 和OutOfMemoryError异常。
5.方法区
所有方法线程共享的一块内存区域,用于存储已经被虚拟机加载的类信息,常量,静态变量等,永久代是它的一种实现。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。
常量池中存储编译器生成的各种字面量和符号引用。字面量就是Java中常量的意思。比如文本字符串,final修饰的常量等。方法引用则包括类和接口的全限定名,方法名和描述符,字段名和描述符等。
Java8版本及以后使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。字符串常量和类文件中常量池放进了JVM的堆中
元空间两个参数:
- MetaSpaceSize:初始化元空间大小,控制发生GC阈值
- MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存
Java8 和 Java8以前
Java8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。
扩展
1.对象的访问
通过句柄访问
直接访问
2.对象的引用
在jdk1.2之后,Java对引用的概念进行了扩充,总体分为4类:强引用、软引用、弱引用、虚引用,这4中引用强度依次逐渐减弱。
- 强引用:指在代码中普遍存在的,类似 Object obj = new Object(); 这类的引用,只有强引用还存在,GC就永远不会收集被引用的对象。
- 软引用:指一些还有用但并非必须的对象。直到内存空间不够时(抛出OutOfMemoryError之前),才会被垃圾回收。采用SoftReference类来实现软引用
- 弱引用:用来描述非必须对象。当垃圾收集器工作时就会回收掉此类对象。采用WeakReference类来实现弱引用。
- 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响, 唯一目的就是能在这个对象被回收时收到一个系统通知, 采用PhantomRenference类实现