avatar

26.JVM之内存结构介绍

0x00 前言

经过上篇的AQS队列同步器的分析,并发编程这块算是暂告一段落,或许之后还会发表一些未研究过的并发编程知识,如:大名鼎鼎的disruptor框架、fork/join框架等,不过这都是后话了。
接下来这几篇都是讲JVM的,都是简单的一些知识(JVM内存结构、垃圾收集算法、垃圾收集器、JVM参数配置等),没什么深度和难度,大多数是一些概念。
你可以这样理解,后面几篇都是水。(๑╹ヮ╹๑)ノ Studying makes me happy
下面让我开始水第一篇。
哦,对了。这里的JVM是jdk1.8之前的~~

0x01 JVM内存结构

  1. JVM内存总览图

    如上图所见,JVM运行时数据区分为:

    • 方法区
    • 虚拟机栈
    • 本地方法栈
    • 程序计数器
      接下来我们一个一个的说把。哦,对了,在说之前,先聊下什么是线程共享,什么是非线程共享(线程私有)
  2. 什么是线程共享?什么又是非线程共享

    • 线程共享
      • 生命周期与java程序运行的生命周期相同,也就是说在Java程序运行时创建,在Java程序结束时销毁,贯穿整个Java生命周期,期间的数据存放也不分线程。
    • 非线程共享
      • 和线程共享相对的就是非线程共享了。非线程共享,与线程的生命周期相同,每个线程创建时都会创建对应的区域。当线程结束时,该区域就会被回收。
  3. 方法区

    • 首先,方法区是所有线程共享的一个区域。其次方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
    • jdk1.8之前,HotSpot虚拟机(oracle)对该区的实现为“永久代(PermGen space)”,1.8后改为元空间(Metaspace)。
    • 根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
    • 和方法区一样,也是线程共享的区域。对于大多数应用来说,堆空间占JVM空间的大部分。几乎所有创建对象的实例都会被分配到该区域,也就是所有通过new出来的对象,都会出现在这里,所以该区域,也是垃圾收集器重点照顾的对象,关于堆空间内存的划分,将会在垃圾回收算法讲解中说到。
    • 根据Java虚拟机规范的规定,当堆区没有空间完成对象实例的分配,将抛出OutOfMemoryError异常。
  4. 虚拟机栈

    • 虚拟机栈,即我们常说的栈,是虚拟机栈中局部变量表部分。是非线程共享的区域(线程私有),也就是说,每个线程会独立拥有一个虚拟机栈。
    • 局部变量表:
      • 存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
    • 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
    • Java虚拟机规范中,虚拟机栈会出现2种异常情况。即
      • StackOverflowError
        • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
      • OutOfMemoryError
        • 如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
  5. 本地方法栈

    • 本地方法栈和虚拟机栈的作用是相似的,同样是线程私有的。它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
    • 与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
  6. 程序计数器

    • 程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,即当前执行到了第几行字节码。
    • 每个线程都有自己的一个程序计数器,各条线程之间计数器互不影响,独立存储。这也就是说,该区域是线程私有的。如果执行的是native方法,那么程序计数器值为空(undefined)
    • 此区域,是唯一一个不会出现OutOfMemoryError的区域。

0x02 总结

这一篇,貌似没什么可以总结的。就是水的有点不好意思。哈哈~~

参考资料:
深入理解Java虚拟机——JVM高级特性与最佳实践


评论