上一篇文章写到了类加载系统,文章参考地址:建议先去了解点前面的再来看这部分内容,不然很可能很蒙圈。https://blog.csdn.net/weixin_46635575/article/details/122569106
一、概述 1、大致概述先看一下大致结构
补充一点知识
进程就是程序运行的过程,它是去调用分配的系统资源的基本单位( *** 作系统以进程来区分,到底哪个进程使用哪个资源,比如你的摄像头的使用),而线程又是进程执行的基本单元,一个进程里面可以包括多个线程。我们的堆区和方法区(也就圆空间和CodeCache JIT(代码缓存))它们是线程共享的,而其他则是线程私有的【这里怎么来形容呢:我们虚拟机是运行的时候,可以理解是一个进程在运行,而这个进行里面假设有5个线程,而方法区和堆则是只有一个,而其他的比如说程序计数器什么的则是有5个】
2、JVM的线程
咱们编写一个代码
public class pcTest { public static void main(String[] args) { int i = 10; int j = 10; int k = i + j; } }
进行反编译
往下面拖得到如下
上面这张图中,左边的0,1,2,3,4,5,6,等它们是PC寄存器的地址,而后面部分上面bipush上面的则是 *** 作指令。
分析一下PC寄存器的作用过程
第一个问题
第二个问题
我是这样理解的,因为我们是java语言是多线程的,实现多线程的机制是按照时间轮巡的方式分配CPU使用,每一个线程去执行的时候所做的任务不同,要完成的功能不同,当第一个线程去调用CPU使用的时候,假设另外一个线程2又要突然去执行另外一个紧张任务,当2执行完了回来继续执行的时候它是要从之前的位置开始的,所有要每一个线程私有一个PC寄存器才能满足响应的需求。
补充一下CPU时间片
可以自己去补充一下并行和并发的区别。
原因一它是我们的重点中的两个原因二可能是它学过C和C++ (2)堆和栈
回顾一下
当然我们的栈也会存储数据的,虽然它不存储对象,但是它会存储对象的引用。
(3)java虚拟机栈
定义
栈帧就是一个方法【它内部还有很多东西的,包括什么局部变量表等(比如我们的main方法启动的时候,它启动了它就会入栈,它如果又去调用了其他方法的时候其他方法又要入栈,用完了就出栈,如果main方法也出栈了,那你的这个程序也执行结束了)】
栈的优点
这里的OOM是异常的简写。
(4)问题:你知道的异常
StackOverflowError异常
至于OOM就不太好演示了
(5)参数Xss的设置栈大小
语法是用-Xss你向设置的数字大小【比如Xss256k,或者Xss128m等】效果
(6)栈存储结构和运行原理
栈是什么
运行原理
栈帧与栈帧之间是不可以相互访问的
如果你的方法正常执行结束,那最后直接return。如果某一个方法有异常,那么它会一层一层的往上抛,直到有处理异常的方法为止,如果都没有处理,那么最后你这个main的线程就会直接挂掉。
背下来:局部变量表,方法返回地址, *** 作数栈,动态链接和一些附加信息(有的数上把动态链接和一些附加信息和方法返回地址叫做帧数据区)
初步认识
字节码方法内部结构
关于Slot的理解
上面这张图中就显示了为什么在静态方法中不能用this引用。
slot的重复利用
案例:静态变量与局部变量的对比
另外一个性能调优的问题
最上面的是有进程,它是去调用系统资源的单位排在进程下面的是线程,线程是进程完成一个个任务的进本单位线程下面就有一个个的栈帧(它是去虚拟机栈的内存结构基本单位)栈帧下面又有五个(局部变量表,方法返回地址, *** 作数栈,动态连接和一些附加信息)这个栈帧的内部结构的局部变量表是一个个的变量曹(slot),所有的基本类型,对象的引用以及this引用都是栈用不同的槽的数量来进行存储的。 (4)栈帧的 *** 作数栈(Operand Stack)
*** 作数栈的内部实现的原理是基于数组实现的具体内容
案例演示:就以刚才的15+8的例子来讲解
看它的指令可以知道,
首先0地址是先压入栈,此时程序计数器也记住你的执行地址为0,接下来要执行的地址位置是2。(此时也就把数据压入栈了)然后转接着指令2去执行,执行就把从 *** 作数15从栈中拿走,放到局部变量表中存放,并且存放的是第二个槽,因为this就占用了一个(此时程序计数器又知道了吓一跳指令是3)接下来开始把我们的8压入 *** 作数栈(然后程序计数器又知道了是5的地址)同样的道理执行下面的执行(程序计数器到6的指令)然后其他的自己分析一下,不管怎么样, *** 作栈的大小在过程中就只有是在把8和15都取出来进行加的一个动作,所以我们的栈的深度就是2了撒。
(5)基于 *** 作数栈的i++和++i面试题
具体内容
我感觉这个结果有点意料之外:我之前背的先加了再用,个一个是用完了再加
另外具体的等到我们后面的文章会细入的讲解,有需要的朋友可以自己找一些文章看。补充一个技术(栈顶缓存技术)
(6)栈帧的动态链接(Dynamic linking)
其实它是指向常量池中的方法引用
看例子
上面后面两张表中的信息是以符号引用的方法存在了我们的字节码文件中了,运行起立的时候会被加载到虚拟机的方法区,而我们在hello()方法里面调用了的hello2方法的时候就是去常量池里面找引用。(有了动态链接就可以是原来的链接变为直接连接)
再来看一张比较细的图
看个题目:就如果没有常量池可以吗
其实答案是可以的,但是你在每个地方都去进行引用处理,那么你多浪费资源啊,咱们写程序的也要考虑内存的大小等内容,代码块的复用等。
而且你直接写到字节码文件里面,那你的字节码岂不是很大,是吧。
动态链接和静态链接
虚方法和非虚方法
特殊之一:动态调用invokedynamic指令
复习一下:重写的本质
虚方法表(为每一个类都维持有一张方法表)
(8)栈帧的方法返回地址(return address)
不一定有,但是有些虚拟机提供了,前面四种是必须提供的
问题一:举列溢出情况
StackOverflowError的异常,可以通过Xss进行设置调整栈的大小,就能保证不出现溢出吗?
答案是不能,有些你本来就无法结束的方法调用的时候,它还是会出现错误的。问题三:栈的内存分配越大越好吗?
并不是,虽然它是可以避免了你内存不足的问题(比如StackOverflowError),但是比如线程里面的资源是有限的,会占用其他的信息,会导致其他的问题。问题四:垃圾回收是否涉及到内存空间吗?
不会,内存回收是不会对虚拟机栈的内存进行回收的。方法中定义的局部变量时否线程安全?
首先线程安全,就是只有一个线程来 *** 作的情况则必是线程安全的。
具体问题,具体分析,比如你不能用StringBuffer来谈,可以用本身它是不安全的StringBuilder来谈。有些定义的参数时不容得的线程来 *** 作的情况,也是线程不安全的。
4、另外运行时数据区的
运行时数据区的其他内容将在下一篇文章中写到,下一篇文章会下次贴出地址。【补充】
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)