JVM 运行时数据区

181 阅读3分钟

运行时数据区

mark

mark

这两张图能够很好的帮大家建立起JVM运行时数据区的图像,接下来让我们一步一步揭开运行时数据区的神秘面纱。

程序计数器

在讲程序计数器之前,我得先扯一下CPU运行相关的知识,我们都知道CPU的运行是需要上下文的,同时CPU其实就是一个不断地执行指令、运算的芯片,为了让CPU的功能正常运行,我们需要有寄存器以及程序计数器来配合CPU的工作。这是计算机的底层。

到了上层的应用程序其实也离不开下层的逻辑,线程是CPU最基本的调度单位,因此线程如果要让CPU运行起来,那么必须为CPU提供相应的指引,于是程序计数器就是这么来的。它是程序控制流的指示器。

我们注意到,程序计数器是私有的,换句话说,我们不希望A线程使用B线程的程序计数器,这是合理的,因为开发者在开发过程中会刻意的控制每条线程该做什么事情,例如A线程负责查找用户信息,B线程负责下单,如果A线程跑了B线程的代码,这不是很荒唐?同理,虚拟机栈与本地方法栈也是这个道理。

虚拟机栈

虚拟机栈描述的是 Java 线程的内存模型。

虚拟机栈加了一个虚拟机的名词,说白了就是一个栈,每个方法在执行的时候就会被JVM加入到栈(虚拟机栈)中,而方法在JVM中使用一个叫栈帧的数据结构来表示,本节的重点不是讲栈帧,如果读者想知道,我以后会专门讲一讲这个数据结构。我们需要知道的是,为了让JVM记住当前线程做了什么,处于什么状态,栈帧需要记录局部变量,方法返回地址等信息。

本地方法栈

这个与虚拟机栈有异曲同工之妙,由于JVM本身其实是由cpp开发的,且JDK源码中有些方法会直接调用本地方法(可以理解为非 Java 代码的方法),因此需要有一个本地方法栈来记录这个过程。

Java 堆

这是JVM运行时数据区的明星名词,这里是一大堆对象生活的区域,几乎所有被new出来的对象都被存放在这里,JDK 8 后,方法区的实现元空间也被放到了堆空间中。

既然 Java 堆 存放了几乎所有的对象,那么这个位置必定是垃圾回收器的工作区,因此 Java 堆也被称为 GC 堆。

方法区

说到方法区,那就有意思了。

方法区与 Java 堆一样是被所有线程共享的。它用于存放:

  • 类型信息
  • 常量
  • 静态变量
  • 即时编译器编译后的代码缓存等数据
  • 运行时常量池

除此之外,这里想跟大家着重讲一下方法区在 JDK 中的变过过程。89

一开始方法区是在 Java 堆中的永久代实现的,但永久代的对象就像永久代这个名字一样,进入永久代位置的对象就很难被清除了,因此垃圾回收器对这个位置的垃圾回收效果并不是很早,导致永久代很容易出现内存泄漏。

为了解决这个问题,官方打算将方法区由永久代实现改在操作系统内存中实现,也被大家称为元空间。JDK 6 的时候官方已经有了逐步放弃永久代的想法,JDK 7 的时候字符串常量池、静态变量等被移出,到了 JDK 8 的时候,这个概念就消失了。

如下图所示:

mark