写点什么

JVM

用户头像
ROOT
关注
发布于: 2020 年 11 月 22 日

JVM

Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制。JVM实现了Java语言最重要的特征:即平台无关性。原理:编译后的 Java 程序指令并不直接在硬件系统的 CPU 上执行,而是由 JVM 执行。JVM屏蔽了与具体平台相关的信息,使Java语言编译程序只需要生成在JVM上运行的目标字节码(.class),就可以在多种平台上不加修改地运行。Java 虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。因此实现java平台无关性。它是 Java 程序能在多平台间进行无缝移植的可靠保证,同时也是 Java 程序的安全检验引擎(还进行安全检查)。

JVM 是 编译后的 Java 程序(.class文件)和硬件系统之间的接口 ( 编译后:javac 是收录于 JDK 中的 Java 语言编译器。该工具可以将后缀名为. java 的源文件编译为后缀名为. class 的可以运行于 Java 虚拟机的字节码。)



image.png



JVM架构(classloader把硬盘上的class文件加载到JVM中的运行时数据区域,但是它不负责这个类文件能否执行,而这个是执行引擎负责的。)

  • 类加载器

  • 启动类加载器

  • 扩展类加载器

  • 应用类加载器

  • 运行时数据区

  • PC程序计数器:一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器, NAMELY存储每个线程下一步将执行的JVM指令,如该方法为native的,则PC寄存器中不存储任何信息。Java 的多线程机制离不开程序计数器,每个线程都有一个自己的PC,以便完成不同线程上下文环境的切换

  • Java虚拟机栈:与 PC 一样,java 虚拟机栈也是线程私有的。每一个 JVM 线程都有自己的 java 虚拟机栈,这个栈与线程同时创建,它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

  • 本地方法栈:与虚拟机栈的作用相似,虚拟机栈为虚拟机执行执行java方法服务,而本地方法栈则为虚拟机使用到的本地方法服务。

  • Java堆:被所有线程共享的一块存储区域,在虚拟机启动时创建,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配。Java堆在JVM启动的时候就被创建,堆中储存了各种对象,这些对象被自动管理内存系统(Automatic Storage Management System,也即是常说的 “Garbage Collector(垃圾回收器)”)所管理。这些对象无需、也无法显示地被销毁。JVM将Heap分为两块:新生代New Generation和旧生代Old Generation

  • 方法区:方法区和堆区域一样,是各个线程共享的内存区域,它用于存储每一个类的结构信息,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。当开发人员在程序中通过Class对象中的getName、isInstance等方法获取信息时,这些数据都来自方法区。方法区也是全局共享的,在虚拟机启动时候创建。在一定条件下它也会被GC。这块区域对应Permanent Generation 持久代。 XX:PermSize指定大小。

  • 运行时常量池:其空间从方法区中分配,存放的为类中固定的常量信息、方法和域的引用信息

  • 执行引擎

  • JIT编译器

  • 垃圾回收器



垃圾回收机制

  1. 标记回收对象

  2. 引用计数法:很难解决对象之间互相循环引用的问题

  3. 可达性分析:通过一系列称为"GC Roots"的对象作为起点,从这些节点开始向下搜索,如果从GC Roots到一个对象不可达,则证明此对象是不可用的

可作为GC Roots的对象包括下面几种(对于Java程序而言,对象基本都位于堆内存中,简单来说GC Roots就是有被堆外区域引用的对象):

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象

  2. 本地方法栈JNI(即一般说的Native方法)引用的对象

  3. 方法区中类静态常量引用的对象

  4. 方法区中常量引用的对象

  5. 四种引用

  6. 强引用

  7. 软引用:内存空间不足,垃圾回收器就会回收

  8. 弱引用:不管当前空间足够与否,都会回收它的内存

  9. 虚引用:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

  10. 垃圾收集算法

a. 标记-清除算法分为“标记”和“清除”两个阶段,执行过程如下图所示:

标记:首先标记出所有需要回收的对象

清除:在标记完成后统一回收所有被标记的对象



image.png



标记-清除算法主要有两个不足:

i. 效率问题,标记和清除的两个过程效率都不高

ii. 标记-清除会产生大量不连续的内存碎片,这会导致在后面需要分配连续的大对象时,无法找到足够大的连续内存而导致不得不提前触发另一次垃圾收集动作

b. 复制算法

复制算法的大致思路如下,其执行过程如下图所示:

i. 首先将可用内存分为大小相等的两块,每次只使用其中的一块。

ii. 当这一块的内存用完了,就将还存活的对象连续复制到另一块上面,然后把使用过的内存空间一次清理掉



复制算法的代价就是将内存缩小为原来的一半



image.png



现在的商业虚拟机都是采用复制算法来回收新生代。

i. 新生代的内存分为一块较大的Eden空间和两块较小的Survivor空间。

ii. 每次使用Eden和一块Survivor,当进行回收时,将Eden和Survivor中还存活的对象一次性复制到另一个Survivor空间上。然后,清理掉Eden和刚刚使用过的Survivor空               间。

iii. HotSpot虚拟机默认Eden和Survivor的大小比例为8 : 1,这样每次新生代可用内存为整个新生代的90% (10% + 80%),只有10%的内存会被浪费。

c. 标记-整理算法

标记-整理算法分为“标记”和“整理”两个阶段,执行过程如下图所示:

i. 标记:首先标记出所有需要回收的对象

ii. 整理:让所有的存活的对象都向一端移动,然后直接清除掉边界以外的内存。



image.png



d. 分代收集算法

分代收集算法就是将Java堆分为新生代和老年代,根据各自的特点采用最适当的收集算法。

i. 新生代中大批对象死去,只有少量存活,就选用复制算法

ii. 老年代中对象存活几率高,没有额外的空间对它进行分配担保,就必须使用标记-清除或者标记-整理算法。



  1. 垃圾回收器



用户头像

ROOT

关注

还未添加个人签名 2019.07.17 加入

还未添加个人简介

评论

发布
暂无评论
JVM