此篇博客主要以笔记的形式,记录笔者在B站《深入理解JVM》课程中学到的知识点。课程地址:《黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓》
上图为JVM内存结构概况图,本篇博客我们将来探究JVM中的虚拟机栈(图片来自课程截图)
1.什么是虚拟机栈?
栈是程序运行时的单位,而堆是存储的单位。
即: 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
同样的,虚拟机栈与数据结构中的栈相同,保持了**“先进后出,后进先出”**的结构特点。就如同手枪弹夹中的子弹,后装入弹夹的子弹会比先装入的先上膛并发射出去,而“子弹”在栈中也有一个专有名词——栈帧,并且每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。(下图是栈的示意图)
2.虚拟机栈的演示
可能刚接触JVM原理的小伙伴们刚读完上面的描述,还无法理解虚拟机栈的用途,那么接下来我们将用具体的代码演示虚拟机栈在程序中的作用。
在上面的代码中,线程主方法main
调用了方法method1
,接着方法method
又调用了方法method2
。
方法入栈时,栈的变化:
- 因为用户先调用了
main
方法,因此main
方法会最先被压入栈中。 - 接着因为
main
方法调用了方法method1
,因此,方法method1
也被压入栈中。 - 又因为方法
method1
调用了方法method2
,因此最后方法method2
,也会被压入栈中。
方法入栈后如下图:
接着是出栈: 当方法method2
执行完后,需要释放内存。这时我们发现为该方法释放内存十分容易,因为method2
现在处于栈的顶部,出栈时无需移动其他元素,直接出栈即可;等到方法method2
释放完内存后,方法method1
执行完并释放内存,最后main
方法也执行完所有操作释放了内存,与此同时,一个Java程序也到此结束~!!(下图是方法出栈的示意图)
3.问题解析
1.垃圾回收是否涉及栈内存?
- 并不涉及,因为栈中的内存会在方法执行结束后就得到释放,无需垃圾回收机制来干涉。
2.栈内存是否越大越好?
- 并不是,因为栈内存变大,会使得线程数减少(因为内存已经固定,需要分配更多得内存空间时,数量就会因此减少),从而使得程序效率降低。
3.方法内的局部变量是否线程安全?
- 如果局部变量是在方法内生命赋值定义的,并且不作为返回值返回时,才是线程安全的。
针对问题3,我们来写三个方法,并对三个方法中的StringBuilder
局部变量是否存在线程安全的问题进行深入的研究:
首先是方法一: 该方法中的StringBuilder
是在内部声明赋值的,并且并没有作为返回值返回,因此,该方法中的StringBuilder
是线程安全的。
其次是方法二: 该方法中的StringBuilder
虽然并没有作为返回值返回,但是其并非在方法内部声明赋值的,而是作为参数传进来的,在参数传递的过程中,该变量可能存在被其他线程修改的问题。因此,该方法中的StringBuilder
是线程不安全的。
最后是方法三: 该方法中的StringBuilder
虽然是在方法内部实现声明赋值的,但是其最后作为了返回值返回,这时其他线程就存在修改该变量的机会。因此,该方法中的StringBuilder
是线程不安全的。
4.栈的内存溢出问题
栈的内存溢出问题,主要分为以下几种情况:
-
栈帧
过多
导致栈内存溢出(如:未正确结束的递归方法)
-
栈帧
过大
导致栈内存溢出
JVM的虚拟机栈笔记写到这就结束啦,在下一篇博客中我们将来探讨JVM的堆以及本地方法栈。