JVM 之 运行时数据区域

前言

为了方便记忆回顾,在这里记录一下 JVM 运行时数据区域的一些知识点。

运行时数据区域

你想输入的替代文字

堆内存是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象数组都在堆上进行分配。这部分空间可通过 GC 进行回收。

虚拟机栈

每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。

本地方法栈

这部分主要与虚拟机用到的 Native 方法相关。比如一个IO操作,最后肯定是会调用到Native的方法的,像字节码是不能直接调用到内核程序的,这里的 Native 的实现可以是C或者C++。

方法区

主要用于存储类的信息、常量池、方法数据、方法代码等,这里的方法区是JVM的一种规范,经常看到永久代或者元空间是其的某种实现。两种会在下面讨论区别。

PC 寄存器

每个线程都有自己的程序计数器,该寄存器中保存当前执行指令的地址。

永久代和元空间

java.lang.OutOfMemoryError: PermGen space 在JDK1.8之前是一个常见异常,这里的 “PermGen space”其实指的就是方法区。在JDK1.8之后,使用了元空间来代替这个永久代。元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

为什么叫元空间,是因为这里面存储的是类的元数据信息,JVM的实现中将类的元数据放入 native memory, 将字符串池和类的静态变量放入Java堆中. 这样可以加载多少类的元数据就由系统的实际可用空间来控制了。

为什么要用元空间来取代永久代?

可能会有疑惑,为什么取代掉永久代呢?它有什么不足点呢?

永久代的不足

主要的一个原因就是类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。第二个原因是永久代会为 GC 回收效率偏低,基本上是强引用。

元空间的好处

默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小。如果没有指定这个参数,元空间会在运行时根据需要动态调整

之前不管是不是需要,JVM都会吃掉那块空间……如果设置得太小,JVM会死掉;如果设置得太大,这块内存就被JVM浪费了。理论上说,现在你完全可以不关注这个,因为JVM会在运行时自动调校为“合适的大小”