【JVM】- 内存模型篇

136 阅读3分钟
  • 内存模型

JDK1.6

JDK1.8

  • 线程私有的:

    • 程序计数器
    • 虚拟机栈
    • 本地方法栈
  • 线程共享的:

    • 方法区
    • 直接内存 (非运行时数据区的一部分)
  • 程序计数器

    • 线程私有
    • 两个作用
      • 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
      • 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
    • 程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
  • Java 虚拟机栈

    • 线程私有
    • 生命周期和线程相同
    • Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有
      • 局部变量表
      • 操作数栈
      • 动态链接
      • 方法出口信息
    • 出现的两种错误
      • StackOverFlowError : 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
      • OutOfMemoryError: 若 Java 虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出 OutOfMemoryError 错误。
  • 本地方法栈

    • 线程私有
    • 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
    • 也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。
    • 线程共享
    • 唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存
    • Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆
    • Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存
    • JDK 7 版本及JDK 7 版本之前(jdk 8 之后移除了方法区,取而代之是元空间)
      • 新生代内存(Young Generation)
      • 老生代(Old Generation)
      • 永生代(Permanent Generation)
  • 方法区

    • 线程共享
    • 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
    • 被称为永久代
    • jdk 1.8将其移除了,换成了元空间,若不指定大小,会耗尽所有可用的系统内存。
    • 运行时的常量池也是方法区的一部分,JDK1.7及之后版本的JVM已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池
  • 永久代 (PermGen) 替换为元空间原因?

    • 整个永久代有一个 JVM本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小
    • 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由MaxPermSize控制了,而由系统的实际可用空间来控制,这样能加载的类就更多了
    • 在 JDK8,合并HotSpot和JRockit的代码时,JRockit从来没有一个叫永久代的东西,合并之后就没有必要额外的设置这么一个永久代的地方了