继上文 :jvm相关知识
感谢观看,获取“jvm相关学习资料”,请关注或私聊,回复"jvm"获取相关学习资源!
程序计数器
程序计数器英文全称(Program Counter Register)是一块非常小的内存空间,几乎可以忽略不计,在虚拟中每个线程都有自己的程序计数器,是线程私有的,并且生命周期与线程一致。在虚拟机的概念模型里字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
作用
当前线程所执行的字节码行号指示器;
特点
运行最快的存储区域;
线程私有:每个线程都会有自己的程序计数器;
不会发生GC(垃圾回收);
唯一在java虚拟机规范中没有规定内存溢出(OutOfMemoryError)情况:因为程序计数器初始化的时候就确定下来了,所以不会发生溢出;
通过javap 可以看出这个这个code就是行号。
因为cpu是通过线程轮流切换并且分CPU处理器执行,通过以上可以看下这个行号就是程序计数器来完成的。
参考:
https://blog.csdn.net/jiangyang100/article/details/90614280
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.1
虚拟机栈
Java虚拟机栈(Java Virtual Machine Stack)也属于线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。---引用《深入理解java虚拟机》
由于java的设计是跨平台,实现一次编码多平台运行,但每个cpu的架构不同,导致由于性移植上的考虑的所以基于栈设计。
作用
用于存储局部变量表、操作数栈、动态连接、方法出口等。
特点
会发生栈溢出(StackOverflowError):因为线程中的计算所需的Java虚拟机堆栈超出允许的范围;
会发生内存溢出(OutOfMemoryError):先尝试动态拓展jvm栈,如果没有足够空间就抛出OutOfMemoryError;
线程私有的:属于每个独立线程;
生命周期与线程相同:真正的生死与共;
HotSpot虚拟机的栈容器,不可以动态扩展;
方法运行时创建栈帧;
先进先出;
栈帧是什么?
栈的基本单位为栈帧,每个线程都有自已的栈,每个执行方法对应一个栈帧,也叫当前栈帧。每一个栈帧都包括了局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息。
以上会在单独篇章详细描述!
基于栈指令集与基于寄存器的区别在哪?
基于栈指令集
可移植性好,不依赖硬件,可跨平台;
使用零地址指令方式分配,避免寄存器分配难题;
设计与实现简单,适合于资源有限的系统;
基于寄存器
可移植性差,指令集完全依赖硬件;
更少指令,执行高效,性能高;
大部分情况下使用的指令是一址地指令、二址地指令、三址地指令;
个人理解
基于栈速度较慢,但是可移植性好,基于寄存器速度快,但是可移植性差。
参考:
http://reader.epubee.com/books/mobile/86/8606b0389a4460967c467d00497154e1/text00192.html
https://www.cnblogs.com/webor2006/p/9833550.html
https://blog.csdn.net/dashuniuniu/article/details/50347149
https://baike.baidu.com/item/%E9%9B%B6%E5%9C%B0%E5%9D%80%E6%8C%87%E4%BB%A4/4118702?fr=aladdin
https://dl.acm.org/doi/10.1145/800020.808261
https://www.cnblogs.com/newAndHui/p/11168791.html
本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈差不多,区别于虚拟机栈为虚拟机执行java方法(字节码)服务,而本地方法栈则是为虚拟机使用本地(Naive)方法服务。
作用
为虚拟机使用本地(Naive)方法服务。
特点
会发生栈溢出(StackOverflowError):因为线程中的计算所需的Java虚拟机堆栈超出允许的范围;
会发生内存溢出(OutOfMemoryError):先尝试动态拓展jvm栈,如果没有足够空间就抛出OutOfMemoryError;
其他与虚拟机栈差不多;
java堆
java堆(java Heap)属于虚拟机所管理最大的一块。并且被所有线程共享的一块区域,堆是运行时数据区,从中分配所有类实例和数组的内存。堆随着虚拟机启动时创建,同时堆的大小可以是固定的也可以根据计算要求动态扩展,需要时扩展,不需要时进行收缩。堆的内存可以不是连续的。
作用
所有的内存都由堆来分配;
所有线程共享的一块内存区域。
特点
虚拟机管理最大的一块;
被所有线程共享;
没有固定大小,可动态拓展。
方法区
方法区(Method Area)别称非堆(head)与java堆一样,实现所有线程共享内存区域;
作用
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
特点
所有线程共享的一块内存区域;
JDK8以后废除永久代的概念,并且用本地内存实现元空间;
方法区!=永久代:JDK8之前是因为设计团队把收集器的分代设计拓展到方法区,导致更容易内存溢出。
运行时常量池
运行时常量池(Runtime Constant Pool)属于方法区中的一部分。主要用于存放编译期生成的各种字面量与符号引用。
相关比较:
运行时数据区域 | 是否发生Error | 是否发生GC |
---|---|---|
程序计数器 | 否 | 否 |
虚拟机栈 | 是 | 否 |
本地方法栈 | 是 | 否 |
方法区 | 是(OOM) | 是 |
堆 | 是 | 是 |
java虚拟机运行时数据域,以上是简要的介绍,后续需要深入了解每个具体详情。
最后
java虚拟机运行时数据域,以上是简要的介绍,后续需要深入了解每个具体详情。
系列文章:
java发展史及虚拟机历史
jvm相关知识
异常解决:jvm配置+OmitStackTraceInFastThrow 导致不打印日志具体信息