JVM类加载和垃圾回收算法详解

文章目录

  • JVM
    • 一、JVM运行流程
      • 1. JVM执行流程
    • 二、JVM运行时数据区
      • 1. 程序计数器(线程私有)
      • 2. 虚拟机栈 (线程私有)
      • 3. 本地方法栈(线程私有)
      • 4. 堆(线程共享)
      • 5. 元空间(线程共享)
        • 运行时常量池
    • 三、JVM类加载
      • 1. 类加载过程
        • 1)加载
        • 2)验证
        • 3)准备
        • 4)解析
        • 5)初始化
      • 双亲委派模型
      • 双亲委派模型优点
    • 四、垃圾回收
        • 1. 死亡对象的判断算法
          • 1)引用计数算法
          • 2)可达性分析
        • 2. 垃圾回收算法
          • 1)标记清除法
          • 2)复制算法
          • 3)标记整理算法
          • 4)分带算法

JVM

一、JVM运行流程

JVM是 Java 运行的基础,也是实现一次编译到处执行的关键,那么JVM是如何执行的呢?

1. JVM执行流程

程序在执行前先要把 Java 代码转换成字节码(.class文件),JVM 需要把字节码通过 类加载器(ClassLoader) 加载到内存中的 运行时数据区(Runtime Date Area) ,而字节码文件是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此,需要特定的命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程需要调动其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是四个主要组成部分的功能与职责。

在这里插入图片描述

小结,JVM 主要通过以下四个部分来执行 Java 程序的:

  1. 类加载器 (ClassLoader)
  2. 运行时数据区(Runtime Data Area)
  3. 执行引擎(Execution Engine)
  4. 本地库接口(Native Interface)

二、JVM运行时数据区

JVM运行时数据区也叫做内存布局,他由以下五个部分组成:

1. 程序计数器(线程私有)

程序计数器保存了下一条要执行的指令的地址。(不是 CPU 的寄存器,而是内存空间)

“下一条指令”指的是 Java 的字节码。(不是 CPU 的二进制机器语言)

注记: 什么是线程私有?

由于 JVM 的多线程是线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此为了切换线程后能够恢复到正确的执行位置,每条线程都需要独立的程序计数器,各线程之间计数互不影响,独立存储。我们就把这类区域成为 ”线程私有“ 的内存。

2. 虚拟机栈 (线程私有)

Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法执行时的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

JVM 虚拟机栈中包含了以下四部分:

  • 局部变量表:存放了编译器可知的各种基本数据类型、对象引用。(存放方法参数和局部变量)
  • 操作栈:每个方法会生成一个先进后出的操作栈。
  • 动态链接:指向运行时常量池的方法引用。
  • 放法返回地址:PC 寄存器的地址。

3. 本地方法栈(线程私有)

本地方法栈和虚拟机栈类似,只不过 Java 虚拟机栈是给 JVM 使用的,而本地方法栈是给本地方法使用的 。

4. 堆(线程共享)

作用:程序中所有创建的对象都保存在堆中。

我们常见的 JVM 参数设置 -Xms 10m最小启动内存是针对堆的,-Xmx 10m最大运行内存也是针对堆的。

ms 是 memory start 的简称,mx 是 memory max 的简称。

堆里面分为两个区域,新生代和老生代。新生代存放新建的对象,当经过一定 GC 次数之后还存活的对象会放入老生代。

新生代又细分为三个区域:一个Eden + 两个Survivor(S0/S1)。

在这里插入图片描述

垃圾回收时会将 Eden 中存活的对象放到没有使用的 Survivor 中,同时把当前的 Eden 和正在使用中的 Servivor 中的对象清除掉。

垃圾回收的细节部分会在下文着重讲述。

5. 元空间(线程共享)

作用:用来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

运行时常量池

是方法区的一部分,存放字面量与符号引用。

字面量:字符串(Java8之后 移动到堆中)、final常量、基本数据类型的值。

符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。

注记:

因为在7之前的版本,方法区又被称为“永久代”,他有固定的大小,容易发生内存溢出,8之后元空间取代方法区,元空间使用的是本地内存,而不是JVM堆内存,这样就可以避免永久代相关的内存溢出的问题。

小结:

在这里插入图片描述

三、JVM类加载

1. 类加载过程

对一个类来说,他的生命周期是这样的:

我们分别来看每个步骤的具体内容:

1)加载

“加载” (Loading)阶段是整个“类加载”(Class Loading)过程中的第一个阶段,他和类加载 Class Loading是不同的,一个是加载 Loading 一个是类加载 Class Loading。

在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取此类的二进制字节流。
  2. 将这个字节流的静态存储结构转化为元空间的运行时数据结构。
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为元空间这个类的各种区域的访问入口。

加载简单来说就是,JVM要读取.class中的内容,并执行里面的命令。

2)验证

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

3)准备

准备阶段是正式为类中定义的变量(即静态变量,被staic修饰的变量)分配内存并设置类变量初始值的阶段。

例如:

public static int value = 123;

他初始化 value 的 int 值为0,而非 123。

4)解析

解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

5)初始化

初始化阶段,Java虚拟机真正开始执行类中编写的 Java 程序代码,将主导权交给应用程序。初始化阶段就是执行类构造器方法的过程。

双亲委派模型

提到类加载机制就不得不提的一个概念就是“双亲委派模型”。

什么是双亲委派模型?

如果一个类收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(他的搜索范围中没有找到所需的类)时,子类加载器才会尝试自己去完成加载。

JVM默认有三个类加载器:

  • BootstrapClassLoader (引用类加载器) 负责加载标准库的类
  • ExtensionClassLoader (扩展类加载器) 负责加载扩展类
  • ApplicationClassLoader (应用程序类加载器) 负责加载第三方库的类/自己写的代码中的类

双亲委派模型优点

  • 避免重复加载类:比如A类和B类都有一个父类C类,那么当A类启动时就会将C类加载起来,那么B类在加载时就不需要在重复加载C类了。
  • 安全性:可以保证 Java 的核心 API 不被篡改,如果没有使用双亲委派模型,而是每个类加载器自己加载的话就会出现一些问题,例如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类,而有些 Object 类又是用户自己提供的,因此安全性就不能得到保证了。

四、垃圾回收

1. 死亡对象的判断算法
1)引用计数算法

定义:给对象增加一个引用计数器,每当有地方引用他时,计数器就+1;当引用失效时,计数器就-1;任何时刻引用计数器为0的对象都是不能再被使用的了,就是对象已“死”。

主流的 JVM 中没有选用引用计数算法来管理内存,最主要的原因是无法解决对象的循环引用问题。

假设下面是我们写的代码:

Test a = new Test();
Test b = new Test();a.t = b;
b.t = a;a = null;
b = null;

最后造成了两个new出来的对象互相计数,而真正的引用已经置空,引用不到这两个对象了,但是无法把他们标记称“垃圾”,因为他们的引用计数器都还不为0。

(和死锁有点类似)

2)可达性分析

Java采用此方法来判断对象是否存活。

该算法的核心思想为:通过一系列称为 “GC Roots“ 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为”引用链“,当一个对象到 GC Root 没有任何任何的引用链相连时(就是不可达),证明此对象是不可用的。

例如:

在这里插入图片描述

对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对象。

在 Java 中,可以作为 GC Root 的对象包含下面几种:

  • 虚拟机栈中引用的对象;
  • 元空间中类静态属性引用的对象;
  • 元空间中常量引用的对象;
  • 本地方法栈中 JNI(Native方法)引用的对象。
2. 垃圾回收算法
1)标记清除法

算法分为 “标记” 和 “清除” 两部分,先是找到所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

问题有两个:

  • 效率问题:标记和清除两个过程的效率都不高
  • 空间问题:标记清除后会产生大量的内存碎片,空间碎片太多就会导致后续程序需要分配较大对象时,无法找到足够连续的内存来存储,不得已提前出发下一次的垃圾清除。

在这里插入图片描述

2)复制算法

复制算法是为了解决 “标记清理” 的效率问题。他将可用内存分为相等大小的两等块,每次只使用其中的一块,当这块内存需要垃圾回收时,就将此区域内还存活的对象复制到另一块区域上面,然后将使用过区域的内存一次清理掉。这样做的好处是对整个半区进行内存回收,内存分配时就不需要考虑内存碎片的情况,只需移动堆顶指针,按顺序分配即可。

3)标记整理算法

是对 “标记清除” 算法的升级,标记过程一致,但清理不是对对象直接回收,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

4)分带算法

分带算法是通过区域划分,对不同的区域采用不同的垃圾回收策略,从而更好的实现垃圾回收。

当前的 JVM 垃圾回收 都是采用的“分代收集”算法。一般是把 Java 堆分为新生代和老生代,在新生代中,又分为一个Eden + 两个Survivor(S0/S1);新创建的对象会放到 Eden 区中,一轮 GC 过后会将 Eden 区和当前使用的 Survivor 区中还存活的对象放到未使用的 Survivor 区中,经过多轮 GC 后还存活的对象,会把他放到老生代,老生代的垃圾回收算法是上面讲到的 “标记整理算法”。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记整理"算法。

问题:

Minor GC 和 Full GC 这两种 GC 有什么不一样吗 ?

  1. Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。

  2. Full GC 又称为 老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479041.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS 17.4 Not Installed

0x00 系统警告 没有安装 17.4 的模拟器,任何操作都无法进行! 点击 OK 去下载,完成之后,依旧是原样! 0x01 解决办法 1、先去官网下载对应的模拟器: https://developer.apple.com/download/all/?q17.4 …

day04 企业级Linux安装及远程连接知识实践

1. 使用传统的网卡命名方式 在启动虚拟机时,按tab键进入编辑模式 添加命令: net.ifnames0 biosdevname0 这样linux系统会使用传统的网卡命名,例如eth0、eth1…… 2. 快照 做系统关键操作时,一定要使用快照(先将系统关机) 3.…

人体特定吸收率 (SAR) 分布建模

ANSYS HFSS 提供了一种建模 SAR 分布的方法! 2020 年对我们所有人来说都是充满挑战的一年,由于 COVID 19 限制和居家隔离,许多工程师不得不推迟开发时间表。ANSYS HFSS 为所有工程师提供了一种在家安全工作的好方法。隔离期间,您…

.NET9 - Swagger平替Scalar详解(四)

书接上回,上一章介绍了Swagger代替品Scalar,在使用中遇到不少问题,今天单独分享一下之前Swagger中常用的功能如何在Scalar中使用。 下面我们将围绕文档版本说明、接口分类、接口描述、参数描述、枚举类型、文件上传、JWT认证等方面详细讲解。…

计算(a+b)/c的值

计算(ab)/c的值 C语言代码C语言代码Java语言代码Python语言代码 💐The Begin💐点点关注,收藏不迷路💐 给定3个整数a、b、c,计算表达式(ab)/c的值,/是整除运算。 输入 输入仅一行&…

PICO 获取设备号 SN码

Unity版本 2020.3.42f1c1PICO SDK版本PICO Unity Integration SDK-3.0.5-20241105Pico设备pico 4ultra 注意 此api暂时只测试企业版本 pico 4ultra 代码 using Unity.XR.PICO.TOBSupport;private void Awake() {bool result PXR_Enterprise.InitEnterpriseService();Debug.L…

【大数据技术基础】 课程 第8章 数据仓库Hive的安装和使用 大数据基础编程、实验和案例教程(第2版)

第8章 数据仓库Hive的安装和使用 8.1 Hive的安装 8.1.1 下载安装文件 访问Hive官网(http://www.apache.org/dyn/closer.cgi/hive/)下载安装文件apache-hive-3.1.2-bin.tar.gz 下载完安装文件以后,需要对文件进行解压。按照Linux系统使用的…

[STM32]从零开始的STM32 FreeRTOS移植教程

一、前言 如果能看到这个教程的话,说明大家已经学习嵌入式有一段时间了。还记得嵌入式在大多数时候指的是什么吗?是的,我们所说的学习嵌入式大部分时候都是在学习嵌入式操作系统。从简单的一些任务状态机再到复杂一些的RTOS,再到最…

DAY133权限提升-Windows权限提升篇溢出漏洞土豆家族通杀全系补丁对比EXP筛选

知识点 1、Web到Win-系统提权-土豆家族 2、Web到Win-系统提权-人工操作 章节点: 1、Web权限提升及转移 2、系统权限提升及转移 3、宿主权限提升及转移 4、域控权限提升及转移 Windows提权: 1、内核溢出漏洞提权 2、数据库类型提权 3、第三方软件…

web day03 Maven基础 Junit

目录 Maven坐标: 依赖排除: 依赖范围: Maven生命周期: 单元测试: Junit入门: 断言: Junit中的常见注解: 概念:Maven 是一款用于管理和构建 Java项目的工具&#…

day18 结构体

有参宏和函数的区别 1.展开时机:有参宏而言,在预处理阶段展开,而函数在调用时才展开 2.内存使用:有参宏而言,占用的是所在函数的空间,而函数在调用时会单独开辟空间 3.效率上:有参宏的效率比…

44.扫雷第二部分、放置随机的雷,扫雷,炸死或成功 C语言

按照教程打完了。好几个bug都是自己打出来的。比如统计周围8个格子时,有一个各自加号填成了减号。我还以为平移了,一会显示是0一会显示是2。结果单纯的打错了。debug的时候断点放在scanf后面会顺畅一些。中间多放一些变量名方便监视。以及mine要多显示&a…

docker 通过Dockerfile自定义的镜像部署Springboot项目

一、镜像结构介绍: 镜像:层(Layer)添加安装包、依赖、配置等,每一次操作都形成新的一层;基础镜像(BaseImage)应用依赖的系统函数库、环境、配置、文件等;入口&#xff0…

全网最早Towards Generalizable Multi-Object Tracking—通用跟踪器的点跟踪CVPR2024

Towards Generalizable Multi-Object Tracking—迈向可推广的多目标跟踪 原标题:Towards Generalizable Multi-Object Tracking 论文链接:https://arxiv.org/pdf/2406.00429 代码链接:https://github.com/qinzheng2000/GeneralTrack.git 作者…

MyBatis框架-动态SQL-XML中的常用标签+特殊字符在XML中的显示

一、if标签、where标签、trim标签、choose标签、set标签、foreach标签 1、问题引入:where关键字和and关键字在动态SQL里面应该如何添加? (1)if标签: test属性的值是判断条件 if标签里面的内容是条件成立时添加到SQ…

探秘嵌入式位运算:基础与高级技巧

目录 一、位运算基础知识 1.1. 位运算符 1.1.1. 与运算(&) 1.1.2. 或运算(|) 1.1.3. 异或运算(^) 1.1.4. 取反运算(~) 1.1.5. 双重按位取反运算符(~~&#xf…

渗透测试笔记—shodan(7完结)

声明: 学习视频来自B站up主 【泷羽sec】有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&am…

2024年最新版Java八股文复习

最新版本Java八股文复习,每天更新一篇,博主正在持续努力更新中~~~ 一、Java基础篇1、怎么理解面向对象?简单说说封装、继承、多态三大特性?2、多态体现在哪几个方面?3、面向对象的设计原则你知道有哪些吗?4…

Notepad++ 替换所有数字给数字加单引号

前言 今天遇到这样一个场景: 要去更新某张表里 code1,2,3,4,5,6 的数据,把它的 name 设置为 ‘张三’ 但是 code在数据库里面的字段类型是 vachar(64),它自身携带索引 原本可以这样写 SQL: update tableA set namezhangsan where code in …

前端图像处理(一)

目录 一、上传 1.1、图片转base64 二、图片样式 2.1、图片边框【border-image】 三、Canvas 3.1、把canvas图片上传到服务器 3.2、在canvas中绘制和拖动矩形 3.3、图片(同色区域)点击变色 一、上传 1.1、图片转base64 传统上传: 客户端选择图片&#xf…