JVM02——虚拟机栈与本地方法栈
栈:线程运行时需要的内存空间,栈帧:每个方法运行时需要的内存。每个线程只能有一个活动栈帧,来对应当前正在执行的方法。
使用idea可以调试获取虚拟机栈信息。
垃圾回收不会涉及栈内存,因为栈的栈帧会随着方法调用而入栈,随着方法结束而出栈,无需进行垃圾回收。
栈的大小可以进行设置,线程栈越大则可以进行嵌套调用的方法层级越多,但是需要在合理区间,不是越大越好。因为计算机的物理内存是有限的,线程中栈的大小设置的越大,可以容纳的线程数就会越少(每个线程都有自己的栈)。
局部变量是方法栈的私有变量,那么方法内的局部变量是不是一定是线程安全的呢?
如果方法内的局部变量没有逃离方法的作用范围,则是安全的。
如果是基本数据类型,则是安全的。
如果是对象类型数据,并且逃离了方法的作用范围,则线程不安全。参考代码demo1,不同线程栈的变量中存放的地址不会彼此干扰,但同一地址的值可以被不同的线程所修改。
public class demo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); new Thread(()-> m2(sb) ).start(); sb.append(1); sb.append(2); } public static StringBuilder m2(StringBuilder sb) { sb.append(1); sb.append(2); return sb; } }
导致栈内存溢出的情况:
- 入栈栈帧过多,如方法递归次数过多。
- 栈帧过大,这种情况很少出现。
值得注意的是,有时候并不是我们自己写的代码导致了栈的内存溢出问题,而是错误使用第三方库的代码时导致了内存溢出问题。
cpu占用很多。
下面分析两个栈相关的案例。
cpu占用率过高
在linux下可以使用
top
来显示cpu被进程占用的情况,定位到占用过高cpu的进程后,使用ps H -eo pid tid %cpu | grep xxx(进程id)
来查看具体是哪个线程导致的问题。最后使用jstack xxx(进程id)
查看进程所有线程对应的id及引起问题的源码行数。注意使用第二步得到线程编号是十进制,而jstack中的线程编号是16进制,需要进行必要的进制换算。线程迟迟得不到结果
依旧使用
jsatck
定位问题,可以定位到类似下图的信息。
本地方法是指非java语言编写的用于与操作系统底层功能交互的方法。本地方法栈用于给本地方法提供内存空间。
java全栈每日必学,不要高估自己一年能做的事,不要低估自己十年能做的事