java | JVM内存结构

image-20211216141829536

1.程序计数器

  • 定义:记住下一条JVM指令执行的地址

  • 特点

    • 线程是私有的,每个线程有一个单独的程序计数器

    • 不会存在内存溢出

 

2.虚拟机栈

  • 1 定义(Java Virtual Machine Stacks)

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

    • 每个栈里面包含多个栈帧,栈帧里装着调用单个方法时方法内的信息(变量等) 递归时每递归一层就会产生一个栈帧,存放相应信息

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

  • 2 问题思考

    • 垃圾回收不涉及栈内存,因为栈帧用完后会自动出栈销毁,不需要进行回收

    • 栈的内存空间分配不是越大越好,内存空间固定,单个栈的内存空间越大,所能划分的栈的个数就越少

    • 方法内的局部变量是线程安全的

      • 什么是线程安全

        在方法内定义一个局部变量,对该局部变量进行操作,且没有返回值,是线程安全的

        public static void m1() {
               StringBuilder sb = new StringBuilder();
               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;

        对一个传入的参数进行操作,

        public static void m2(StringBuilder sb) {
               sb.append(1);
               sb.append(2);
               sb.append(3);
               System.out.println(sb.toString());
          }

        定义一个静态量,两个方法都对这个量进行操作,是线程不安全的

        • 总结:如果方法内局部变量没有逃离方法的作用访问,他是线程安全的;如果是局部变量引用了对象,并且逃离了方法的作用范围,那么就是线程不安全的

  • 3 栈的内存溢出 StackOverflowError

    • 栈帧过多导致内存溢出:递归层数过多

    • 栈帧过大导致栈内存溢出:一个方法内包含了过多的数据

  • 4 线程的运行诊断

    public static void main(String[] args) throws JsonProcessingException {
       //新建一个部门对象
           Dept d = new Dept();
       //设置部门的名字
           d.setName("Market");
       
       //新建一个员工对象
           Emp e1 = new Emp();
       //设置员工1名字
           e1.setName("zhang");
       //设置员工部门为d
           e1.setDept(d);
       
           Emp e2 = new Emp();
       //设置员工2名字
           e2.setName("li");
        //设置员工部门为d
           e2.setDept(d);
       
       //设置部门里有e1,e2两个员工
           d.setEmps(Arrays.asList(e1, e2));
       //转换java
           ObjectMapper mapper = new ObjectMapper();
          System.out.println(mapper.writeValueAsString(d));
      }

    在转换对象过程中,{name:"Market",emps:[{name:"zhangsan",dept:{name:{} ,dept{name:{},…}部门添加员工,而员工中又有一个部门对象,部门对象中又有员工,无限循环,造成内存溢出。

    image-20211216151757857

3.本地方法栈

和操作系统交互的方法

4.堆 Heap

  • 通过new创建关键字,创建的对象都会使用堆内存

  • 是线程共享的,堆中的对象都需要考虑线程安全问题(堆是线程不安全的)

  • 有垃圾回收机制

5.方法区

  • 定义:存的是和类相关的信息,逻辑上是堆的一部分 实际不一定

  • 方法区内存溢出(元空间内存溢出:原空间使用的是系统内存)MetaSpace

  • 运行时常量池

    • 常量池:一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数信息等等

    • 运行时常量池:常量池是.class文件中的,当该类被加载,它的常量池信息就会被放入运行时常量池,并把里面的符号变成实际内存地址

posted @ 2022-02-08 15:12  尽深  阅读(35)  评论(0编辑  收藏  举报