补充一个点:在运行时数据区中,灰色的为单独线程私有的,红色的为多个线程共享的,即:
不同的JVM对于内存的划分方式和管理机制存在着部分差异。这里给出一张完整的运行时数据区图。🤱
看完上面的两张图,我想应该对JVM中所谓的运行时数据区有个大概印象了吧。下面👇会给大家再给大家带来一些粗略的讲解哈。
当我们通过前面的:类的加载-> 验证 -> 准备 -> 解析 -> 初始化这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区 🤸♂️
运行时数据区,Runtime Data Area,用于保存java程序运行过程中需要用到的数据和相关信息;经常说的把数据读到内存,包括类加载之后的信息,从磁盘读取文件信息等。即:Java虚拟机在执行Java程序的过程中,会将涉及的数据划分到不同的内存区域去管理。
课间休息会
程序计数器是用于存放下一条指令所在单元的地址的地方。
当执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。
JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。
程序计数器是线程私有内存,是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。
JAVA代码编译后的字节码在未经过JIT(实时编译器)编译前,其执行方式是通过“字节码解释器”进行解释执行。**简单的工作原理为解释器读取装载入内存的字节码,按照顺序读取字节码指令。**读取一个指令后,将该指令“翻译”成固定的操作,并根据这些操作进行分支、循环、跳转等流程。
例如:
使用javap -c -verbose ClassCode.class 命令反编译出来结果为:
从上面的描述中,可能会产生程序计数器是否是多余的疑问。
因为沿着指令的顺序执行下去,即使是分支跳转这样的流程,跳转到指定的指令处按顺序继续执行是完全能够保证程序的执行顺序的。假设程序永远只有一个线程,这个疑问没有任何问题,也就是说并不需要程序计数器。但实际上程序是通过多个线程协同合作执行的。
首先我们要搞清楚JVM的多线程实现方式。JVM的多线程是通过CPU时间片轮转(即线程轮流切换并分配处理器执行时间)算法来实现的。也就是说,某个线程在执行过程中可能会因为时间片耗尽而被挂起,而另一个线程获取到时间片开始执行。当被挂起的线程重新获取到时间片的时候,它要想从被挂起的地方继续执行,就必须知道它上次执行到哪个位置,在JVM中,通过程序计数器来记录某个线程的字节码执行位置。因此,程序计数器是具备线程隔离的特性,也就是说,每个线程工作时都有属于自己的独立计数器。即私有性,每个线程都拥有私有的程序计数器
其实在上一段文字中已经写了,这里写个缩句哈。
因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。
JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。
由于CPU时间片轮限制,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。
这样必然导致经常中断或恢复,如何保证分毫无差呢?每个线程在创建后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响。
还是决定手画几张图来帮助大家来理解记忆:
我想看完这个图,大家对pc 程序计数器 为什么是私有是有个大概的理解了吧。