前言
本章 只介绍jvm内存机构的基本概念 用于自己记录和学习,基于JDK1.8, 虚拟机 HotSpot
JVM内存模型
程序计数器
程序计数器是一个记着当前线程所执行的字节码行号指示器
JVM采用CPU时间片轮转算法来调度多线程,当被挂起的线程 重新获取时间片时,它需要知道上次执行到哪里才可以继续执行。
为了确保线程切换后能恢复到正确执行位置,每个线程都有一个独立的程序计数器,互不影响,也就是说 程序计数器是线程私有的。
如果执行Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Natice 方法,计数器值为Underfined
程序计数器不会发生内存溢出 OOM
栈
JVM中的栈包括 Java虚拟机栈 和本地方法栈,其区别就是 Java虚拟机栈为Jvm执行 Java 方法,本地方法栈为JVM 执行Native方法,两者作用极其相似 ,在在HotSpot 中 虚拟机栈与本地方法栈被合并,本篇主要介绍Java虚拟机栈。
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧 ,用于存放 局部变量表,操作数栈,动态链接和方法出口,每一个方法被调用直至执行完成的过程,就对应这个一个栈帧在虚拟机栈中从入栈到出栈的过程。
个人理解 栈里面记录的都是各种操作 比如 加减运算等(纯属个人理解)
栈决定了函数调用的深度。这也是慎用递归调用的原因。递归调用时,每次调用方法都会创建栈帧并压栈。当调用一定次数之后,所需栈的大小已经超过了虚拟机运行配置的最大栈参数,就会抛出 StackOverflowError 异常。
此处就不做详细介绍感兴趣的可以阅读
https://zhuanlan.zhihu.com/p/45354152,以及https://blog.csdn.net/rongtaoup/article/details/89142396
堆
堆是Java虚拟机管理的内存最大的一块存储区域。堆内存被所有线程共享,几乎所有的对象实例都会存储在堆中分配,(逃逸分析 与标量替换 不在堆上 感兴趣可以研究下)
Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。默认分配关系是8:1:1
年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。
老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。
方法区
方法区同Java堆一样是被所有线程共享的区间,用于存放已经被虚拟机加载的类信息,常量,静态变量,可以理解为编译器编译后的字节码,更具体的说 ,静态变量+常量+类信息 +运行时常亮池存在方法区中,
方法区同 Java 堆一样是被所有线程共享的区间,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。更具体的说,静态变量+常量+类信息(版本、方法、字段等)+运行时常量池存在方法区中
JDK1.8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。
Java 中包装类 Byte、Short、Integer、Long、Character、Boolean 都实现了常量池技术, Float 和 Double 则没有实现。 Byte、Short、Integer、Long、Character 这 5 种整型的包装类也只是在对应值在 -128~127 之间时才可使用对象池。
下期我们将结合代码详细讲解常量池。
END