JVM学习笔记——内存结构篇( 二 )


虚拟机栈简介我们的虚拟机栈和栈的基本原理相同,但存储的东西就不尽相同了:

  • 虚拟机栈也是绑定线程的,每个线程有且仅有一个虚拟机栈
  • 虚拟机栈中存储着栈?。?可以存在有多个栈帧,栈帧就是每个方法运行时所需要的内存
我们给出虚拟机的概念:
  • 每个线程运行时所需要的内存,称为虚拟机栈
  • 每个线程只能有一个活动栈帧 , 对应着当前正在执行的那个方法
  • 每个栈由多个栈?。‵rame)组成,对应着每次方法调用时所占用的内存
虚拟机栈详细介绍我们给出一段Java代码来进行展示:
package cn.itcast.jvm.t1.stack;/** * 演示栈帧 */public class Demo1_1 {public static void main(String[] args) throws InterruptedException {method1();method3();}private static void method1() {method2(1, 2);}private static int method2(int a, int b) {int c =a + b;return c;}private static int method3() {return 1;}}我们进行简单的介绍:
  • 这个程序就是一个线程
  • 这三个方法分别就对应着一个栈帧
  • 我们调用main,mian进入栈,main中又调用method1,method1进入栈,method1调用method2 , 所以method2进入栈
  • 注意我们的method3不包含在method1的循环中,所以我们会先将前置栈帧都排除后,然后在main栈帧的上方进行累加method3
我们在执行过程中如果采用debug模式是可以看到Frames,这个就是表示的栈?。?
JVM学习笔记——内存结构篇

文章插图
虚拟机栈问题解释我们针对虚拟机栈提出了三个问题,下面进行解释:
  1. 垃圾回收是否会涉及栈内存
/*答案是:否 , 因为栈是属于线程内内存,栈具有自动回收功能*/
  1. 栈内存是否是越大越好
/*答案是:否,如果jvm设置的内存过大,就会导致其它程序所占用的内存小 。*/
  1. 方法内的局部变量是否线程安全
/**答案是:根据实际情况而定,我们通过判断作用范围来进行线程安全判断首先我们需要介绍两个词汇:- StringBuffer 用于多线程,保证多线程安全,但效率较慢- StringBuilder 用于单线程,无法保证但线程安全,效率较快**/// 我们下面给出一个简单示例:// 示例1:下面的变量x是属于方法中的变量,属于局部变量,因此不会出现线程安全问题package cn.itcast.jvm.t1.stack;/** * 局部变量的线程安全问题 */public class Demo1_18 {// 多个线程同时执行此方法static void m1() {int x = 0;for (int i = 0; i < 5000; i++) {x++;}System.out.println(x);}}// 示例2:下面我们通过StringBuilder来进行安全问题测试(因为StringBuilder不具有线程安全保护)// m1:StringBuilder创建在方法内部,也只在方法内部使用,属于局部变量,不具有线程安全问题// m2:StringBuilder来自于外部,变量作用范围越界,具有线程安全问题// m3:StringBuilder返回至外部,变量作用范围越界 , 具有线程安全问题package cn.itcast.jvm.t1.stack;/** * 局部变量的线程安全问题 */public class Demo1_17 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append(4);sb.append(5);sb.append(6);new Thread(()->{m2(sb);}).start();}public static void m1() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);sb.append(3);System.out.println(sb.toString());}public static void m2(StringBuilder sb) {sb.append(1);sb.append(2);sb.append(3);System.out.println(sb.toString());}public static StringBuilder m3() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);sb.append(3);return sb;}}虚拟机栈内存溢出问题虚拟机栈在默认情况下为1024K,正常情况下不会溢出,但如果出现异常可能导致溢出
首先我们介绍一个改变虚拟机栈的方法:
// 在配置运行环境的Environment variables中进行配置(如下修改为256k)-Xss256k然后我们介绍两种溢出情况:
  1. 栈帧过多
// 正常情况下我们的栈?。ǚ椒ǎ┚退阍俣嘁膊换岬贾履诖嬉绯觯?但是如果我们发生了无限递归异常呢?// 我们在这个方法中递归调用本身,就会导致不断有栈帧加入到虚拟机栈中,最终导致虚拟机栈内存溢出package cn.itcast.jvm.t1.stack;/** * 演示栈内存溢出 java.lang.StackOverflowError * -Xss256k */public class Demo1_2 {private static int count;public static void main(String[] args) {try {method1();} catch (Throwable e) {e.printStackTrace();System.out.println(count);}}private static void method1() {count++;method1();}}
  1. 栈帧过大
/*我们仅仅是来解释这个溢出想法但实际上我们的默认虚拟机栈大小为1M,是不可能出现栈帧过大的情况的~*/

推荐阅读