一、JVM 主要组成部分:

        JVM的主要包含两个组件和两个子系统,分别为:

  • (1)Native Interface(本地库接口):与native lib(本地方法库)交互,融合其他编程语言为Java所用,是与其它编程语言交互的接口
  • (2)Runtime data area(运行时数据区):即常说的JVM内存
  • (3)Class loader(类加载子系统):根据全限定类名装载class文件到运行时数据区的方法区中
  • (4)Execution engine(执行引擎子系统):也叫解释器,负责解释class指令,再提交给操作系统执行

        通过上面的图,我们可以大致知道Java代码是如何运行的:首先通过编译器将Java源代码转换成字节码,接着类加载系统把字节码加载到运行时数据区的方法区内,再使用执行引擎将字节码翻译成底层系统指令,最后交由 CPU 去执行,而这个过程中可能需要调用其他语言的本地库接口来实现整个程序的功能。

字节码只是 一套 JVM 指令集规范,并不能直接交给底层操作系统去执行,因此需要通过执行引擎将字节码翻译成底层系统指令

二、JVM 内存:

        JVM 在执行 Java 程序时,会将内存划分为若干个不同的数据区域,不同的区域用途不同,创建和销毁时间也不相同。但在 JDK1.8 版本之后对运行时数据区域做了些修改,下面我们分别来看看修改前后的内存区域是怎么样的。

1、JDK1.8之前的JVM内存区域:

2、JDK8之后的JVM内存区域

3、各区域的的作用:

(1)程序计数器:

        当前线程执行的字节码的行号指示器,记录当前线程执行到程序的哪个位置,通过改变计数器的值,可以选取下一条需要执行的字节码指令。该区域是线程私有,且是唯一一个不会发生OOM的区域。

(2)虚拟机栈:

        描述 Java 方法执行的内存模型,方法执行时都会创建一个栈帧,每个方法从调用到执行完成,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。用于存储局部变量表、操作数栈、动态链接、方法出口等信息。该区域线程私有,生命周期与线程的生命周期相同。

(3)本地方法栈:

        本地方法栈和虚拟机栈的作用相似,区别是本地方法栈为Native方法服务,而虚拟机栈为Java方法服务,该区域也是线程私有。

(4)Java堆:

        用于存储对象实例,是占用内存最大的区域,可划分为新生代和老年代,新生代又可细分为 Eden区、From Survivor区、To Survivor区。

在 HotSpot 中,对象在堆内存布局分成三部分:对象头,实例数据,对齐填充。

① 对象头:包括两部分的信息:

  • 运行时数据:存储对象自身的运行数据,如哈希码,GC代年龄,锁状态、线程持有的锁、偏向线程ID等。
  • 类型指针:即对象指向它的类型数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组,那对象头中还必须有一块用于记录数组长度的数据。

② 实例数据:是对象真正存储的有效信息,是在程序代码中所定义的各种类型的字段内容,相同宽度的字段会被分配到一起。

③ 对齐填充:并不是必然存在的,仅起着占位符的作用。

(5)方法区:

        该区域被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码(即class文件)等数据,同时,方法区中有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该区域不需要连续的内存,并且可以动态扩展,动态扩展失败会抛出 OOM 异常,对该区域进行垃圾回收的主要目标是对常量池的回收和对类型的卸载,但是一般比较难实现。

        方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。JDK8 之前,Hotspot 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永久代的静态变量和常量池移至堆内存,其他内容移至元空间,元空间直接在本地内存分配。

        那为什么要使用元空间取代永久代的实现?主要是为了方便管理方法区:

① 永久代与堆所使用的物理内存是连续的。对于永久代,由于类和方法等信息比较难确定大小,所以指定永久代的大小比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出,并且每次 Full GC 之后永久代的大小都会改变,如果动态生成很多 class 的话,就很可能出现 OOM,毕竟永久代的空间配置有限。

② JDK8之后,方法区存在于元空间,物理内存不再与堆内存连续,而是直接存在于本地内存中,理论上机器内存有多大,元空间就有多大。

③ 字符串存在永久代中,容易出现性能问题和内存溢出。

④ 永久代会为 GC 带来不必要的复杂度,回收效率偏低,因为方法区中类静态属性引用的对象、常量引用的对象都是 GC Roots 对象


参考文章:https://www.cnblogs.com/czwbig/p/11127124.html

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐