原创

JVM

温馨提示:
本文最后更新于 2022年10月19日,已超过 926 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

一 jvm概述

1. 什么是jvm

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

2.概述

Java虚拟机有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统。

Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。

Java虚拟机不仅是一种跨平台的软件,而且是一种新的网络计算平台。该平台包括许多相关的技术,如符合开放接口标准的各种API、优化技术等。Java技术使同一种应用可以运行在不同的平台上。Java平台可分为两部分,即Java虚拟机(Java virtual machine,JVM)和Java API类库。 [1]

3.体系结构概览

img

img

二 类装载器

1. 虚拟机自带三个

(1) 启动类加载器(bootstrap) C++

(2) 扩展类加载器(extension) java

(3) 应用程序类加载器(appClassLoader),也叫系统类加载器,加载当前应用的classpath的所有类

2.用户可以实现Classloader 自定义加载器(一般不用)

双亲委派机制:使用类A.class时,从顶级父类一层一层往下找,找到了使用,未找到报空指针异常。(java程序有健壮性)

img

沙箱安全机制

img

img

三 本地接口

为了融合不同的编程语言为java所用(可以使用java调用其他不同语言的方法)

在本地方法栈中登记 native方法,在 execution engine执行时加载本地方法库

img

四 PC寄存器(程序计数器)

  1. 线程私有

  2. 内存占据很小,可以忽略不计

  3. 用于记录程序执行的指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址)

  4. 如果执行一个native方法,则这个计数器是空的

img

五 方法区

  1. 线程共享

  2. JDK版本相关,1.7以前为永久代,1.8后是元空间

  3. 存储每一个类的结构信息,例如运行时的常量池,字段,和方法数据,构造函数和普通方法。以及static方法和属性。

img

六 栈区

  1. 线程私有,生命周期跟随线程的生命周期。8中基本类型变量+对象引用变量+实例方法都是在函数的栈内存中分配

  2. 不参与垃圾回收

  3. 主要存储3类数据:

    (1) 本地变量:输入参数和输出参数以及方法内的变量

    (2) 栈操作:记录入栈,出栈的操作

    (3) 栈帧数据:包括类文件,方法等等

img

  1. 栈运行原理

    (1) 先进后出

    (2) 每个方法在运行的时候都会创建一个栈帧,用于存储局部变量,操作数栈,动态链接,方法出口等信息

img

七 对象生命周期

  1. 堆内存分配

img

  1. 对象生成后,进入eden区,当eden满了之后,发生一次GC(YGC),清空全部eden区,如果eden区的对象还存在引用,则放入S0区(幸存区from),如果S0和S1中的对象不存在引用,则一起回收掉,如果依然存在引用,则保留,将其生命周期+1(一般到15会进入老年代区)

  2. 当老年代区占满后,会发生一次Full GC ,清除掉老年代区中没有被引用到的对象,full gc时间会更长。(一般优化的原则是尽量保证程序不进行full gc)

  3. 当老年代区的对象占满,新生代区持续向老年代区存放对象时,会发生内存溢出。

img

八 JVM参数调整

  1. JVM虚拟机大小

最大值Xmx,默认为当前运行物理机的内存1/4

最小值Xms, 默认为当前运行物理机内存的1/64

img

  1. 实际生产中, Xmx和Xms设置成一样,避免内存忽高忽低,造成卡顿。

  2. 输出gc日志参数 -XX:+PrintGCDetails

调整最大内存和最小内存 -Xmx1024m -Xms1024m

多个配置之间使用空格隔开

img

img

九 垃圾回收四大算法

1.引用计数法:基本不用

**实现**:当一个对象存在引用的时候,就在该对象的引用计数上加1,当不存在引用的时候,为0,代表可以回收。

**缺陷**:

每次对对象赋值都要维护引用计数器,且计数器本身也有一定的消耗

较难处理循环引用

img

2.复制算法(YGC使用)

一般发生在年轻代区。

当年轻代区发生GC时,会全盘扫码eden区和S0(from)区,当eden区的对象没有被引用时,则清除,有引用的,则放入S0区,如果S0区的对象依然被引用,则将被引用的对象放入S1(区),如果S1区已满,则将S1区的对象全部放入老年代区。最后,S1区的对象会全部复制到S0区,此时S1(to)区为空,S0区是当前GC后的全部存活对象。(印证谁空谁是to区)

**缺点**:耗费空间

3.标记清除算法

分为两个阶段,第一阶段标记出要回收的对象,第二阶段进行统一回收

img

缺点:两次扫描,耗时严重。会产生内存碎片

img

4.标记压缩(老年代区使用,和标记清除算法混合使用)

一般和标记清除算法一起使用,先标记,再整理,最后清除

缺点:耗时,需要整理移动所有存在引用的对象

img

总结

内存效率: 复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。

内存整齐度: 复制算法=标记整理算法>标记清除算法。 内存利用率: 标记整理算法=标记清除算法>复制算法。 可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记/整理算法相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/清除多了一个整理内存的过程

十 JVM相关问题:

1. JVM内存模型以及分区,需要详细到每个区放什么?

2. 堆里面的分区:Eden,survival from to,老年代,各自的特点?

3. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方?

4. Minor Gc 与Full GC分别在什么时候发生?


正文到此结束