「JVM 系列」- 带你搞懂JVM之运行时数据区域

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战html

前言

JVM(Java Virtual Machine):爪哇虚拟机java

JVM内存结构:运行时数据区域git

事实上,对于JVM上层,还存在一个由Oracle定制的Java虚拟机规范,咱们常说的JVM通常是指具体的某个JVM规范的具体实现。好比咱们常用的Java虚拟机HotSpot,JDK1.8使用的就是HotSpotweb

> java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
复制代码

好比咱们常常搞混的,JDK1.8以前的永久代、JDK1.8以后的元空间,这篇文章是永久代,到了那篇文章又是元空间的,实际上他们两个均可以看做是JVM规范中方法区的实现数组

本文针对JVM的内存区域,尽量详细的解剖缓存

事实上,JVM的知识多且杂,容易混淆,对此,我强烈建议你点赞收藏加关注,逛完P B站,趁着精神状态良好,来复习一遍JVM想必也是极好服务器


JVM总体架构

JVM内存区域,又称运行时数据区域,就是下图红框框起来的部分:markdown

JVM总体架构图:数据结构


JVM内存结构

主要分为如下区域:架构

  1. 线程私有的:程序计数器,本地方法栈,虚拟机栈
  2. 线程公有的:方法区,堆

程序计数器(线程私有)

程序计数器是一块较小的内存空间,能够看做是当前线程所执行的字节码的**行号指示器;**它是惟一一个在 JVM 规范中没有规定任何 OutOfMemoryError 状况的区域

程序计数器的做用

当多个线程在特定的时间内同时执行的时候,一个线程不可能一直占用着CPU 的资源,这样就会形成频繁的上下文切换,为了线程切换回来后,能恢复到正确的位置继续执行,每一个线程都有本身的程序计数器

字节码解释器工做时就是经过改变这个计数器的值来选取下一条须要执行的字节码指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理等


程序计数器里存储的是什么?

任什么时候间一个线程都只有一个方法在执行,也就是所谓的当前方法。若是当前线程正在执行的是 Java 方法,程序计数器记录的是 JVM 字节码指令地址,若是是执行 native 方法,则是未指定值(undefined)


虚拟机栈(线程私有)

每一个线程在建立的时候都会建立一个虚拟机栈,其内部保存一个个的栈帧,每一次方法调用都会有一个对应的栈帧被压入栈中,每个函数调用结束后,都会有一个栈帧被弹出,遵循“先进后出/后入先出”的原则

以下图,方法1调用了方法2,因而方法2把方法1压在了身下,这个过程就是压栈;当前方法执行完毕后(return或者异常),就会进行出栈操做


栈里面存什么

虚拟机栈里面保存栈帧,每一个栈帧中保存了方法的局部变量表、操做数栈、动态连接、方法出口信息

在这里大概说一下栈里面的几个东西是干吗的,详细内容下回分解

  • 局部变量表:一组变量值存储空间,主要用于存储方法参数和定义在方法体内的局部变量,好比方法内的String abc = "abc";对象引用(reference 类型,它不一样于对象自己,多是一个指向对象起始地址的引用指针,也多是指向一个表明对象的句柄或其余与此对象相关的位置)
  • 操做数栈:主要用于保存计算过程的中间结果,同时做为计算过程当中变量临时的存储空间
  • 动态连接:每一个栈帧都保存了一个能够指向当前方法所在类的运行时常量池, 目的是: 当前方法中若是须要调用其余方法的时候, 可以从运行时常量池中找到对应的符号引用, 而后将符号引用转换为直接引用,而后就能直接调用对应方法, 这就是动态连接
  • 方法返回地址:方法正常return退出时,调用者的 PC 计数器的值做为返回地址,即调用该方法的指令的下一条指令的地址;经过异常退出的,返回地址是要经过异常表来肯定的,栈帧中通常不会保存这部分信息

虚拟机栈会抛出什么异常

虚拟机栈的内存大小能够是动态的,也能够是固定大小的

  • StackOverFlowError:当设置为固定大小时,如果压栈超出了最大的容许值就会抛出该异常
  • OutOfMemoryError:当设置为动态扩展时,如果扩展时没法向系统申请到足够的内存时会报该异常

### 本地方法栈(线程私有)

本地方法栈的功能与虚拟机栈相似,会抛出的异常也同样,二者的区别在于,虚拟机栈是为Java方法服务的,而本地方法栈是为Native方法服务的

并非全部 JVM 都支持本地方法。由于 Java 虚拟机规范并无明确要求本地方法栈的使用语言、具体实现方式、数据结构等。若是 JVM 产品不打算支持 native 方法,也能够无需实现本地方法栈

在 Hotspot JVM 中,直接将本地方法栈和虚拟机栈合二为一


堆(线程共享)

Java 虚拟机所管理的内存中最大的一块,Java 堆是全部线程共享的一块内存区域,在虚拟机启动时建立。此内存区域的惟一目的就是存放对象实例几乎全部的对象实例以及数组都在这里分配内存,正因如此,Java 堆也是垃圾收集器管理的主要区域,所以也被称做GC 堆(Garbage Collected Heap)

堆内存的划分

为了高效的进行垃圾回收,能够发现,堆被划分红了三个区域:

  1. 新生代(Young Generation):新对象和没达到必定年龄的对象都在新生代,新生代又被分为Eden、Survivor0,Survivor1
  2. 老年代(Old Generation):被长时间使用的对象,年龄到了之后会进入老年代,老年代的内存空间应该要比年轻代更大
  3. 元空间(图中的Perm区):JDK1.8之前不叫元空间,叫永久代,无论是元空间仍是永久代,均可以看做是JVM规范中方法区的实现。方法区有个别名叫非堆(Non-Heap),目的应该是与 Java 堆区分开


如何配置堆内存

在启动Java程序的时候,能够用参数指定堆的大小:

  • -Xms:设定堆的起始内存,默认状况下,初始堆内存大小为:电脑内存大小/64
  • -Xmx:设定堆的最大内存,默认状况下,最大堆内存大小为:电脑内存大小/4

分享一个小zi si,咱们一般会将 -Xmx-Xms 两个参数配置为相同的值,其目的是为了可以在垃圾回收机制清理完堆区后再也不须要从新分隔计算堆的大小,从而提升性能

例:

java -Xmx1024m -Xms1024m -jar test.jar
复制代码

堆最容易出现 OutOfMemoryError错误,有两个比较常见的错误信息:

  1. OutOfMemoryError: GC Overhead Limit Exceeded:这是JVM在垃圾回收时用了太多时间而且回收的空间不多的时候会报出的错误
  2. OutOfMemoryError: Java heap space:这是建立对象是,发现堆中的内存不足以存放新的对象就会出现这个报错

经过对象在堆中的生命周期进一步了解堆

  1. 当建立了一个新的对象时,首先会在新生代的Eden区分配内存,此时JVM会给这个对象一个年龄(能够经过-XX:MaxTenuringThreshold参数设置)
  2. 当新来的对象很大,而Eden空间不足时,就会触发Minor GC,把不须要的干掉,再将新对象放入Eden区
  3. Eden经历一次垃圾回收后的对象会被放入Survivor0中,而且给它的年龄+1
  4. 若是再次触发垃圾回收,Survivor0 中的对象会被移入 Survivor1,年龄再次+1
  5. 若是对象的年龄超过了咱们配置的进入老年代的年龄阈值,对象就会被分配到老年代
  6. 老年代不多进行GC,当老年代内存不足时,会触发MajorGC,若到了这一步依然没法对新来的对象进行保存,就会出现OOM(内存溢出)

方法区

方法区,是JVM规范定义的一个概念,不管是JDK1.8以前的永久代,仍是JDK1.8以后的元空间,都属于规范的一种是实现。能够把方法区看做Java代码中的接口,而永久代和元空间则是它的具体实现

方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等

如何配置元空间的大小

  1. -XX:MetaspaceSize:设置元空间的初始大小
  2. -XX:MaxMetaspaceSize:设置元空间的最大大小

因为元空间是直接使用系统的物理内存的,若是不指定元空间额度大小,随着类的建立,虚拟机可能会耗尽全部的可用系统内存

一样的,因为元空间使用的是系统内存,虽然任有可能溢出,可是出现的几率会小不少,当元空间溢出时会报错 OutOfMemoryError: MetaSpace

须要注意的是:

  • 对于一个 64 位的服务器端 JVM 来讲,其默认的 -XX:MetaspaceSize 的值为20.75MB,这就是初始的高水位线,一旦触及这个水位线,Full GC 将会被触发并卸载没用的类(即这些类对应的类加载器再也不存活),而后这个高水位线将会重置,新的高水位线的值取决于 GC 后释放了多少元空间。若是释放的空间不足,那么在不超过 MaxMetaspaceSize时,适当提升该值。若是释放空间过多,则适当下降该值
  • 若是初始化的高水位线设置太低,上述高水位线调整状况会发生不少次,经过垃圾回收的日志可观察到 Full GC 屡次调用。为了不频繁 GC,能够将 -XX:MetaspaceSize 设置为一个相对较高的值。

Reference

JVM 基础 - JVM 内存结构 | Java 全栈知识体系 (pdai.tech)

JavaGuide - Java内存区域

CSDN:什么叫作JVM与JVM规范

相关文章
相关标签/搜索