【JVM】JVM内存模型详解
一、JVM是什么?
JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。
二、JVM内存模型图
三、JVM运行数据区详解
- JVM运行数据区分为虚拟机栈、本地方法栈、程序计数器、堆区、元空间(方法区)五部分。其中虚拟机栈、本地方法栈和程序计数器属于线程私有,而堆、元空间属于线程共享。
- 虚拟机栈中存储方法、方法中的局部变量、以及运行时数据,本地方法栈和虚拟机栈类似,只不过本地方法中栈存储的是本地方法的一些数据信息,程序计数器用来表示程序代码的执行位置,每一线程工作时,都会开辟自己的虚拟机栈、本地方法栈、和程序计数器。
- 堆区,存储对象信息,以及数组,是所有线程共享区域、
- 元空间存储类加载的相关信息,以及静态变量、常量、运行时常量池等。
下面,以代码演示各个位置存储的信息:
//1.首先类加载器将类加载到原空间public class Application { //main线程--> main的线程栈,也就是虚拟机栈 //2.执行main 方法,将main方法压入栈 public static void main(String[] args) throws Exception { //3.load方法压入虚拟机栈 load(); System.in.read(); //保证程序不要退出 } public static void load() { //4.在堆区创建Config对象,同时在栈区存储对象的引用地址,指向堆区 Config config = new Config(); //5.将loadData()方法压入栈 config.loadData() ; } }
public class Config { //1.静态变量存储在元空间 public static Manager mdnagerl = new Manager(); //2.实例变量/对象变量 和类对象一起存储在堆区 private int a;//3.loadData()存储在方法区 public String loadData() { return "abc"; }}
四、JVM运行数据区各部分特点及作用
- 堆
堆用来存放对象和数组,只要是堆中的对象,就可以被所有线程共享(静态变量、静态常量、字符串存储在堆中的老年代里)。Java7 版本中将永久代的静态变量和运行时常量池转移到堆中存放的。
堆是 JVM 上最大的内存区域。垃圾回收操作的对象就是堆。
堆空间一般是程序启动时就申请了,一般设置成可伸缩的。 随着对象的频繁创建,堆空间占用的越来越多,就需要不定期的对不再使用的对象进行回收,这就是GC。
对于基本数据类型对象(如byte、short、int、long、float、double、char),在方法体内声明时,会直接分配在栈中,其它情况都会分配在堆中。
对于普通对象来说,JVM 会首先在堆上创建对象,然后在其他地方使用它的引用。比如,把这个引用保存在虚拟机栈的局部变量表中。但是在开启了逃逸分析时,如果发现某个对象只会在方法内部使用,则可能会将该对象经过标量替换后也存在栈中。
堆的几个重要参数:
-Xms:堆的最小值(初始值,默认单位是:字节,要求是1024的整数倍);
-Xmx:堆的最大值;
-Xmn:新生代的大小;
-XX:NewSize;新生代最小值(初始值);
-XX:MaxNewSize:新生代最大值;
虚拟机栈
Java虚拟机栈是当前线程在执行方法时存储所需的数据、指令、返回地址的一种栈结构(先进后出)。它的生命周期与线程保持一致。提一句:静态变量不入栈。
每调用一个方法就会在栈里加入一个栈帧。调用的方法执行完了,对应的栈帧就会出栈。栈帧里分为4个区域,这4个区域就包含了执行Java方法时的全部内容。这个4个区域分别是:局部变量表、操作数栈、动态连接、方法出口。
虚拟机栈默认1M。 如果我们不断的往虚拟机栈中入栈帧,但是就是不出栈的话,那么这个虚拟机栈就会溢出。
程序计数器
由于现在都是多线程运行,而一个CPU在同一时刻只能运行一个线程,多个线程只能交替运行。程序计数器的作用就是记录当前线程下一条要运行的指令,这样保证了线程在切换回来时能回到正确的位置继续开始执行
程序计算器是唯一不会发生内存溢出的地方。如果正在执行的是Native 方法,由于不是JVM执行,则这个计数器值为空(Undefined)
方法区(元空间)
方法区也是一个线程共享的内存区。
方法区存储的内容有:类型信息(比如类全称、父类全称)、域信息(域名称、域修饰符private等)、方法信息(方法名称、方法修饰符、返回类型等)、字面量(字面量包括文本字符串、八种基本类型的值 、被声明为final的常量等)。
假如两个线程都试图访问方法区中的同一个类信息,而这个类还没有加载进 JVM,那么此时就只允许一个线程去加载它,另一个线程必须等待。
方法区是 JVM 对内存的“逻辑划分”,在 JDK1.7 及之前将方法区称为“永久代”,是因为在 HotSpot 虚拟机中,设计人员使用了永久代来实现了 JVM 规范的方法区。在 JDK1.8 及以后使用了元空间来实现方法区。
Java8 使用元空间替代永久代,是为了融合 HotSpot JVM 与 JRockit VM ,因为 JRockit 没有永久代。
元空间大小参数设置:
jdk1.7 及以前:-XX:PermSize;-XX:MaxPermSize;
jdk1.8 以后:-XX:MetaspaceSize; -XX:MaxMetaspaceSize ;如果不设置参数,则只受本机总内存的限制
来源地址:https://blog.csdn.net/qq_57549633/article/details/126495630
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341