3 栈

那我们java中每个线程运行的时候需不需要为每个线程划分独立的空间呢?

  • 答案肯定是的。我们的虚拟机栈呢就是线程运行时需要的空间,一个线程需要一个栈。

那栈内组成元素又是什么呢?

  • 栈帧

栈帧又代表什么呢?

  • 那大家想,我的线程他最终是要去执行代码的。那这些代码呢都是由一个个的方法来组成的。所以线程运行的时候,每个方法他运行时需要的内存,我们就称之为一个栈帧。

那大家想,我方法需要什么内存啊?方法里面的参数,变量不都得需要内存吗。每个方法调用完了,他还需要一个返回地址,那这些信息都是需要占用内存的。所以每个方法执行时,我们就需要预先把这些内存给他分配好。

我方法在执行的时候,就会把对应的栈帧压进栈。当方法执行完之后,再把栈帧出栈。也就是释放这个方法所占用的内存。

那有没有可能一个栈内有多个栈帧存在呢?

  • 有的,假如我调用方法1,这一个栈帧,然后方法1间接调用其它方法,这又是一个栈帧

那方法三调用结束就把栈帧3出栈,回到方法2,方法2调用结束之后栈帧2出栈,回到方法1。

3.1 栈的演示

java virtural machine stacjs(java虚拟机栈)

  • 每个线程运行时所需的内存,称为虚拟机栈

  • 每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存

  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。

往下执行

再往下

再往下:

继续往下执行

3.2 栈问题辨析1

1:垃圾回收是否涉及栈内存?

  • 栈内存在方法调用的时候产生,方法调用结束就销毁掉了,不需要垃圾回收来解决。

2:栈内存分配越大越好吗?

  • 栈内存我们指定大小 -Xss size 

  • 栈内存划的大,反而会让你的线程数变少。因为我们物理内存的大小是一定的。比如说我一个线程使用的是占内存1M,总内存是500M。理论上线程可以有500个。但是如果你每个线程的内存设置为2M,那线程数就只有250个。

3:方法内的局部变量是否是线程安全的?

  • 那看变量安全不安全,我们就看这个变量他对多个线程来说是共享的还是每个线程私有的。我们看一段代码来解释这个问题。image-20210917211619917

    假如有两个线程同时来执行这个方法,那会不会造成这个方法里面的x混乱呢?

    • 不会,因为我们x变量是方法内的局部变量,我们说一个线程对应一个栈帧。那这两个线程的栈帧是不一样的,相当于每个线程都有自己私有的局部变量x,互不影响。

那假如把x改为静态的呢?

  • 这个时候因为是静态的,那么两个线程都会去操作这个变量,并且操作完之后还会把结果返回,那就会造成混乱,最后的结果可能不是5000.

3.3 栈问题辨析3

你要看一个变量是不是线程安全的,你不能仅仅看她是不是局部变量,还得看他是不是逃离了我这个方法的作用范围。

例1:

虽然说sb是在方法内部,是局部变量,但是他被作为返回结果返回了,逃离了我这个线程的作用范围。那其他线程就有可能拿到这个变量,去并发的修改他。就不是线程安全的。如果

3.4 栈-内存溢出

什么情况会导致栈内存溢出?

  • 栈帧过多导致栈内存溢出。(递归调用的时候没有设置结束条件)

    思考补充:为什么这里用exception 就打印不出结果,而用throwable就可以打出结果?

  • 当json转换格式的时候里面数据互相调用死循环导致栈溢出

  • 栈帧过大导致栈帧内存溢出。(不太容易出现这种情况)

 

如何避免去写栈内存溢出的代码?

3.5 线程诊断

案例1:cpu占用过多

定位:

  • 用top命令定位哪个进程对cpu占用过高

  • ps H -eo pid, tid % cpu | grep 进程id (用ps进一步查看是哪个线程干的好事)

  • jstack 进程id (查看进程中的线程,注意jstack输出的进程编号是16进制的)

    • 可以根据线程id找到线程,进一步定位到有问题的源码行数

 

案例二:程序运行很长时间没有结果

  • 线程之间发生死锁

原文地址:https://www.cnblogs.com/YXBLOGXYY/p/15332736.html