JVM内存结构

结合一段 java 代码的执行理解内存划分

图片[1]- JVM内存结构-不念博客
  • 执行 javac 命令编译源代码为字节码
  • 执行 java 命令
    1. 创建 JVM,调用类加载子系统加载 class,将类的信息存入方法区
    2. 创建 main 线程,使用的内存区域是 JVM 虚拟机栈,开始执行 main 方法代码
    3. 如果遇到了未见过的类,会继续触发类加载过程,同样会存入方法区
    4. 需要创建对象,会使用内存来存储对象
    5. 不再使用的对象,会由垃圾回收器在内存不足时回收其内存
    6. 调用方法时,方法内的局部变量、方法参数所使用的是  JVM 虚拟机栈中的栈帧内存
    7. 调用方法时,先要到方法区获得到该方法的字节码指令,由解释器将字节码指令解释为机器码执行
    8. 调用方法时,会将要执行的指令行号读到程序计数器,这样当发生了线程切换,恢复时就可以从中断的位置继续
    9. 对于非 java 实现的方法调用,使用内存称为本地方法栈(见说明)
    10. 对于热点方法调用,或者频繁的循环代码,由 JIT 即时编译器将这些代码编译成机器码缓存,提高执行性能

说明

  • 加粗字体代表了 JVM 虚拟机组件
  • 对于 Oracle 的 Hotspot 虚拟机实现,不区分虚拟机栈和本地方法栈

会发生内存溢出的区域

  • 不会出现内存溢出的区域 – 程序计数器
  • 出现 OutOfMemoryError 的情况
    • 堆内存耗尽 – 对象越来越多,又一直在使用,不能被垃圾回收
    • 方法区内存耗尽 – 加载的类越来越多,很多框架都会在运行期间动态产生新的类
    • 虚拟机栈累积 – 每个线程最多会占用 1 M 内存,线程个数越来越多,而又长时间运行不销毁时
  • 出现 StackOverflowError 的区域
    • JVM 虚拟机栈,原因有方法递归调用未正确结束、反序列化 json 时循环引用

方法区、永久代、元空间

  • 方法区是 JVM 规范中定义的一块内存区域,用来存储类元数据、方法字节码、即时编译器需要的信息等
  • 永久代是 Hotspot 虚拟机对 JVM 规范的实现(1.8 之前)
  • 元空间是 Hotspot 虚拟机对 JVM 规范的另一种实现(1.8 以后),使用本地内存作为这些信息的存储空间
图片[2]- JVM内存结构-不念博客

从这张图学到三点

  • 当第一次用到某个类是,由类加载器将 class 文件的类元信息读入,并存储于元空间
  • X,Y 的类元信息是存储于元空间中,无法直接访问
  • 可以用 X.class,Y.class 间接访问类元信息,它们俩属于 java 对象,我们的代码中可以使用
图片[3]- JVM内存结构-不念博客

从这张图可以学到

  • 堆内存中:当一个类加载器对象,这个类加载器对象加载的所有类对象,这些类对象对应的所有实例对象都没人引用时,GC 时就会对它们占用的对内存进行释放
  • 元空间中:内存释放以类加载器为单位,当堆中类加载器内存释放时,对应的元空间中的类元信息也会释放
© 版权声明
THE END