这是我参与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的知识多且杂,容易混淆,对此,我强烈建议你点赞收藏加关注,逛完
PB站,趁着精神状态良好,来复习一遍JVM想必也是极好服务器
JVM内存区域,又称运行时数据区域,就是下图红框框起来的部分:markdown
JVM总体架构图:数据结构
主要分为如下区域:架构
程序计数器是一块较小的内存空间,能够看做是当前线程所执行的字节码的**行号指示器;**它是惟一一个在 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)
为了高效的进行垃圾回收,能够发现,堆被划分红了三个区域:
在启动Java程序的时候,能够用参数指定堆的大小:
-Xms
:设定堆的起始内存,默认状况下,初始堆内存大小为:电脑内存大小/64-Xmx
:设定堆的最大内存,默认状况下,最大堆内存大小为:电脑内存大小/4分享一个小zi si,咱们一般会将
-Xmx
和-Xms
两个参数配置为相同的值,其目的是为了可以在垃圾回收机制清理完堆区后再也不须要从新分隔计算堆的大小,从而提升性能
例:
java -Xmx1024m -Xms1024m -jar test.jar
复制代码
堆最容易出现 OutOfMemoryError
错误,有两个比较常见的错误信息:
OutOfMemoryError: GC Overhead Limit Exceeded
:这是JVM在垃圾回收时用了太多时间而且回收的空间不多的时候会报出的错误OutOfMemoryError: Java heap space
:这是建立对象是,发现堆中的内存不足以存放新的对象就会出现这个报错-XX:MaxTenuringThreshold
参数设置)方法区,是JVM规范定义的一个概念,不管是JDK1.8以前的永久代,仍是JDK1.8以后的元空间,都属于规范的一种是实现。能够把方法区看做Java代码中的接口,而永久代和元空间则是它的具体实现
方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等
-XX:MetaspaceSize
:设置元空间的初始大小-XX:MaxMetaspaceSize
:设置元空间的最大大小因为元空间是直接使用系统的物理内存的,若是不指定元空间额度大小,随着类的建立,虚拟机可能会耗尽全部的可用系统内存
一样的,因为元空间使用的是系统内存,虽然任有可能溢出,可是出现的几率会小不少,当元空间溢出时会报错 OutOfMemoryError: MetaSpace
须要注意的是:
-XX:MetaspaceSize
的值为20.75MB,这就是初始的高水位线,一旦触及这个水位线,Full GC 将会被触发并卸载没用的类(即这些类对应的类加载器再也不存活),而后这个高水位线将会重置,新的高水位线的值取决于 GC 后释放了多少元空间。若是释放的空间不足,那么在不超过 MaxMetaspaceSize
时,适当提升该值。若是释放空间过多,则适当下降该值-XX:MetaspaceSize
设置为一个相对较高的值。