zoukankan      html  css  js  c++  java
  • 深入理解JVM(2)——运行时数据区

     JVM: 1、完成了 class 文件到机器语言的翻译转换。2、可以进行内存管理。

    1、运行时数据区

    1.1、程序计数器

    记录当前线程正在执行的字节码指令的地址,如果正在执行的是 Native 方法,这个计数器值则为空。

    1.2、虚拟机栈

    每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、 动态链接、 方法返回地址等信息。方法调用到执行完成的过程,就对应一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。

    可以通过虚拟机参数 -Xss 来指定每个线程的虚拟机栈内存大小: -Xss128k

    该区域可能抛出以下异常:

    • 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
    • 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。

    1.2.1、局部变量表

    是一片逻辑连续的内存空间,最小单位是Slot,用来存放方法参数和方法内部定义的局部变量。虚拟机通过索引定位的方式使用局部变量表,索引值范围从0开始至局部变量表最大的Slot数量。

    虚拟机没有明确指明一个Slot的内存空间大小。但是boolean、byte、char、short、int、float、reference、returnAddress类型的数据都可以用32位空间或更小的内存来存放。这些类型占用一个Slot。Java中的long和double类型是64位,占用两个Slot。(只有double和long是jvms里明确规定的64位数据类型)

    reference类型:表示对一个对象实例的引用,它可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置。通过这个引用至少做到两点,一是从此引用能查找到对象在Java堆中的数据存放的起始地址索引,二是从此引用能查找到对象所属数据类型在方法区中的存储的类型信息。

    returnAddress类型:指向了一条字节码指令的地址。

    执行实例方法,局部变量表中第0位索引的Slot默认是方法所属对象实例的引用,方法中可以通过关键字“this”来访问。 方法参数则按照参数表顺序,占用从1开始的局部变量Slot。之后再根据方法体内部定义的变量顺序和作用域分配其余的Slot。

    1.2.2、操作数栈

    每个栈帧都包含一个叫做操作数栈(操作栈)的后进先出的栈。

    方法刚开始执行时,这个方法的操作数栈是空的,在方法的执行过程中,有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。向其他方法传参的参数,也存在操作数栈中。其他方法返回的结果,返回时存在操作数栈中。

    操作数栈中元素的数据类型必须与字节码指令的序列严格匹配。

    本来栈桢作为虚拟机栈的一个单元,应该是栈桢之间完全独立的。但是,大多虚拟机的实现里进行了一些优化:为了避免过多的方法间参数的复制传递、方法返回值的复制传递等一些操作,就让一部分数据进行栈桢间共享。

    1.2.3、动态链接

    Class 文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接

    1.2.4、方法返回地址

    当一个方法被执行后有两种方式退出这个方法:

    • 正常完成出口:执行引擎遇到任意一个方法返回的字节码指令。
    • 异常完成出口:遇到异常,并且这个异常没有在方法体里得到处理。

    无论采用何种退出方式,在方法退出后,都需要返回方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用于恢复它的上层方法的执行状态。一般来说方法正常退出,调用者的 PC 计数器的值就可作为返回地址,栈帧中很可能保存的就是这个计数器的值。而方法异常退出时,返回地址要通过异常处理器来确定,这时候栈帧中一般不会保存这部分信息。 

    方法退出的过程实际上等同于将当前栈帧出栈。因此退出时的操作可能有:恢复上层方法的局部变量表和操作数栈,如果有返回值则把他压入调用者栈帧的操作数栈中。调整 PC 计数器的值以指向方法调用的指令的后一条指令。

    1.3、本地方法栈

    本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。

    1.4、堆

    所有对象都在这里分配内存,是垃圾收集的主要区域("GC 堆")。

    现代的垃圾收集器基本都是采用分代收集算法,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法,可以将堆分成两块:

    • 新生代(Young Generation)
    • 老年代(Old Generation)

    堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。

    可以通过 -Xms 和 -Xmx 两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。

    1.5、方法区

    用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。

    对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。

    HotSpot 虚拟机把它当成永久代来进行垃圾回收。但是很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。

    1.5.1、运行时常量池

    运行时常量池是方法区的一部分。

    Class 文件中的常量池(编译器生成的各种字面量(Literal)符号引用)会在类加载后被放入这个区域。

    除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

     1.6、直接内存

    在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存(Native 堆),然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。

    这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

     2、元空间替换永久代

    在JDK1.7及以前版本的 HotSpot 虚拟机中,是采用永久代(PermGen space)来实现 JVM 规范中的方法区的。到了JDK1.8方法区的实现变成了元空间(Metaspace)。

    其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用转移到了 native heap;字面量、类的静态变量转移到了java heap。

    2.1、为什么移除持久代

    • 它的大小是在启动时固定好的——很难进行调优。-XX:MaxPermSize,指定太小容易造成永久代OOM。
    • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
    • Oracle 可能会将HotSpot 与 JRockit 合二为一。

    2.2、元空间配置参数

    -XX:MetaspaceSize=N

    这个参数是初始化的Metaspace大小,该值越大触发Metaspace GC的时机就越晚。随着GC的到来,虚拟机会根据实际情况调控Metaspace的大小,可能增加上线也可能降低。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,-XX:Metaspacesize为21810376B(大约20.8M)。

    -XX:MaxMetaspaceSize=N

    这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。

    -XX:MinMetaspaceFreeRatio=N

    当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。

    -XX:MaxMetasaceFreeRatio=N

    当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。

    -XX:MaxMetaspaceExpansion=N

    Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。

    -XX:MinMetaspaceExpansion=N

    Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约为330KB)。

  • 相关阅读:
    Python (time、datetime、random、os、sys、shutil)模块的使用
    ELK6+filebeat、kafka、zookeeper搭建文档
    JDBC-用Java语句操作数据库
    数据库对象
    sql之子查询
    函数之sql语句
    sql之select语句
    oracle 数据库
    网络编程
    多线程——多线程的运行轨迹,线程的生命周期,线程的常用方法,线程的安全问题,死锁,线程间的通信
  • 原文地址:https://www.cnblogs.com/boomoom/p/9953910.html
Copyright © 2011-2022 走看看