当前位置: 首页 > news >正文

JVM类加载的过程和JVM垃圾回收机制

文章目录

  • 一、JVM类加载的过程
    • 1.1类加载的基本流程
      • 1.1.1加载
      • 1.1.2验证
      • 1.1.3准备
      • 1.1.4解析
      • 1.1.5初始化
    • 1.2双亲委派模型
  • 二、JVM垃圾回收机制
    • 2.1找到垃圾
      • 2.1.1引用计数(比如Python,PHP中用到)
      • 2.1.2可达性分析(比如Java中用到)
    • 2.2释放垃圾
      • 2.2.1标记清除
      • 2.2.2复制算法
      • 2.2.3标记整理
      • 2.2.4分代回收

一、JVM类加载的过程

1.1类加载的基本流程

Java代码会被编译成.class文件(里面包含了一些字节码),JVM会把.class文件读取到内存中并对其进行解析、构造类对象(这个过程叫类加载),类加载完成之后就会在内存中得到类对象,后续要构造这个类的实例都是基于类对象来进行展开的。

1.1.1加载

找到.class文件,打开文件,读取文件内容。从Java代码中往往会得到某个类的“全限定类名”(比如java.lang.String),JVM会根据这个“全限定类名”在一些指定的目录范围内去查找对应的.class文件,找到对应的.class文件就能够把这个.class文件打开并且读取里面的内容。

1.1.2验证

验证.class文件里的内容是否符合要求。
.class文件是二进制格式的文件,里面的某个字节都是有某些特定含义的。java标准文档:https://docs.oracle.com/javase/specs/index.html里说明了一个.class文件的格式是怎样的,.class文件里应该要包含哪些内容。

1.1.3准备

给类对象分配内存空间。这个内存空间的大小是根据上一步的验证的结果来确定的。这里只是分配内存空间,还没有初始化内存空间,此时这个内存空间上的数值全是0,此时如果打印类的static成员就会打印出0。

1.1.4解析

针对类对象中包含的字符串常量进行一些初始化操作。

java代码中用到的字符串常量在编译之后会进入到.class文件中。

比如java代码中有:final String a = “hello”;
编译之后,.class文件的二进制指令中也会有一个a这样的引用被创建出来,由于引用本质上保存的是一个变量的地址,在.class文件中,因为文件不涉及到内存地址,所以.class文件中的a就会先被设置成一个“文件偏移量”,通过这个“文件偏移量”可以找到hello这个字符串所在的位置,当我们把这个类真正加载到内存的时候,再把这个“文件偏移量”替换回真正的hello的内存地址。

在这里插入图片描述
如上图所示,假设在.class文件中,文件开头到hello开头的距离是100个字节,就称hello这个字符串在.class文件中的“文件偏移量”为100。文件开头到test开头的这100个字节里也会有一条指令,这条指令描述了String a = @100,这里的@100表示“文件偏移量”。当.class文件加载到内存中的时候,test这时的内存地址为0x12,String s = @100也会把@100这个“文件偏移量”替换成hello这个字符串真实的内存地址,这个替换的过程就是“解析”阶段要完成的主要工作。这个替换过程也叫把“符号引用”(“文件偏移量”)替换成“直接引用”(内存地址)。

1.1.5初始化

针对类对象进行初始化,即把类对象中的各个属性都设置好。
初始化好static成员。
执行静态代码块。
加载父类。

1.2双亲委派模型

双亲委派模型属于类加载的第一个步骤“加载”过程中的其中一个环节,即根据“全限定类名”找到.class文件。

JVM中内置了三个类加载器(程序员也可以手动创建出新的类加载器):
①BootStrap ClassLoader
②Extension ClassLoader
③Application ClassLoader
这三个类加载器彼此之间存在一个父子关系,即Application ClassLoader是子、Extension ClassLoader是父、BootStrap ClassLoader是爷,这个父子关系不是继承,而是这几个类加载器里都有一个parent这样的属性,这个parent属性指向一个父“类加载器”。

类加载的第一个步骤“加载”过程中找.class文件的过程:
①给定一个类的全限定类名,比如java.lang.String。

②以Application ClassLoader作为入口根据全限定类名开始执行查找对应的.class文件的逻辑。

③Application ClassLoader不会立即扫描自己负责的目录(Application ClassLoader复责的目录是当前项目对应的目录和第三方库对应的目录),而是把查找的任务交给他的父亲Extension ClassLoader。

④Extension ClassLoader也不会立即扫描自己负责的目录(Extension ClassLoader负责的目录是JDK中的一些扩展库对应的目录(JDK厂商会在标准之外做一些扩展)),而是把查找的任务交给它的父亲BootStrap ClassLoader。

⑤BootStrap ClassLoader也不会立即扫描自己负责的目录(BootStrap ClassLoader负责的是标准库对应的目录),而是把查找的任务交给它的父亲,结果发现没有父亲,因此BootStrap ClassLoader只能扫描自己负责的目录,如果类是标准库中的类,那么在BootStrap ClassLoader这个类加载器中就能找到对应的.class文件,此时查找.class文件的过程就结束了。
如果类不是标准库中的类,则查找.class文件的任务就会交给孩子Extension ClassLoader去执行。

⑥Extension ClassLoader就会扫描自己负责的目录,如果找到对应的.class文件,则查找结束,就执行后续的类加载操作;如果没找到,则把任务交给孩子Application ClassLoader执行。

⑦Application ClassLoader就会扫描自己负责的目录,如果找到对应的.class文件,则查找结束,就执行后续的类加载操作;如果没找到,就会抛出ClassNotFoundException。

双亲委派模型的目的是为了维护类被加载的优先级。

二、JVM垃圾回收机制

Java中new一个对象,就是一次“动态内存申请”。
动态表示运行时(程序运行起来才能确定内存大小),静态表示编译时(编译时就能确定内存大小)。
编译时:int a[5],a数组占据多少内存,在编译过程中就能确定下来,一个int是4字节,5个int就是20字节。

在C语言中使用malloc申请的内存在使用完之后需要通过free来释放,在C++中使用new申请的内存需要通过delete来释放。

Java给出了垃圾回收机制(GC),让JVM自动把不再使用的内存回收掉。而不用手动回收内存,大大降低了程序员的心智负担。

局部变量的生命周期是跟随栈帧的生命周期走的,方法执行结束栈帧销毁,局部变量所对应的内存也就释放了。
静态成员变量的生命周期是整个程序的生命周期,是类对象中的一部分,类加载之后是不会卸载的,所以静态成员变量无需释放。
所以GC回收的是堆上的对象。

GC分为两个步骤:

2.1找到垃圾

有两种主流方案:

2.1.1引用计数(比如Python,PHP中用到)

new出来的对象单独安排一块空间来保存一个计数器,这个计数器用来进行引用计数,这个计数器描述了这个对象有几个引用在指向它。
比如:
{
Test t = new Test();
Test t2 = t;
}
出了{}之后,t和t2就被销毁了,引用计数就归0了。当对象的引用计数为0时,此时这个对象就可以视为垃圾了。

但Java没有使用引用计数,因为引用计数有两个缺陷:
①比较浪费内存。因为每个new出来的对象都要单独安排一个计数器来保存它的引用计数,计数器至少要占据两个字节的内存空间,如果对象很少或者对象很大这时影响不大;如果对象很小并且很多这时计数器占据的空间就不容忽视了,内存就被浪费了很多。
②循环问题。
比如:
class A {
public A t;
}
class Test {
public static void main(String[] args) {
A a = new A();
A b = new A();
a.t = b;
b.t = a;
a = null;
b = null;
}
}
在这里插入图片描述
此时a和b两个引用已经被销毁了,new出来的两个对象已经无法被其它代码访问到,但是它们的引用计数不为0,这时这两个对象是不能回收的,第一个对象引用了第二个对象,第二个对象引用了第一个对象。要想拿到第一个对象就要先拿到第二个对象,要想拿到第二个对象就要先拿到第一个对象,这构成了逻辑上的循环错误。

2.1.2可达性分析(比如Java中用到)

可达性分析本质上是时间换空间。有一个/一组线程周期性地扫描代码中的所有对象,从一些特定的对象出发,尽可能地进行遍历访问(比如类似于N叉树遍历),把所有能够被访问到的对象都标记成“可达”,不能被访问到的未被标记的对象就是垃圾了。
可达性分析开始遍历访问的起点对象有很多,比如:局部变量中引用的对象、常量池中引用的对象、方法区中类静态属性引用的对象……,这些起点对象统称为GCRoots。
可达性分析是周期性进行的,因为某个对象是否是垃圾是会随着代码的执行而发生改变的(比如这个对象现在不是垃圾,代码执行了一段时间之后就变成垃圾了)。所以可达性分析比较消耗系统资源,导致系统时间开销较大,相比之下引用计数通过计数器来衡量当前对象是否是垃圾,比较精准,时间开销比较小。

2.2释放垃圾

有三种基本思路:

2.2.1标记清除

把垃圾对象直接释放掉,但这个方案非常不好,因为这会产生很多的内存碎片。我们释放内存是为了让其它代码能够申请内存,而申请内存时我们申请到的都是连续的内存空间。如果使用标记清除使用了一段时间,那么内存中出现内存碎片的情况将会非常严重,导致内存申请变得十分困难。

2.2.2复制算法

把内存分成两份,一次只用其中的一半。通过复制的方式把有效的对象归类到另一半,再统一释放原来那一半的所有空间。
复制算法可以有效解决内存碎片问题,但这个方案也有缺点:
(a)内存要浪费一般,内存利用率低。
(b)如果有效的对象非常多,那么拷贝的开销就会很大。

2.2.3标记整理

这个方法既能够解决内存碎片的问题,又能够解决复制算法中内存利用率低的问题,但拷贝的开销和复制算法差不多。
标记整理类似于顺序表删除元素时的搬运操作。在内存空间中把有效的对象一个一个地往内存空间的前面搬运,然后把内存空间后面的空间回收掉。
在这里插入图片描述

2.2.4分代回收

JVM释放内存的方法,是上述三种基本思路的结合体,即分代回收。
把堆分成两部分,这两部分不是等分的。左边称为新生代,右边称为老年代。新生代中有一个幸存区和一个伊甸区,幸存区里等分为两部分。
在这里插入图片描述
①刚new出来的新的对象放在伊甸区,从对象诞生到可达性分析扫描开始,这个过程虽然时间不长(往往是毫秒~秒级别),但在这个时间里大部分对象都会成为垃圾,即大部分对象都活不过一轮GC。

②伊甸区中经过一轮GC后仍然可达的对象,就会通过复制算法被拷贝到幸存区。然后释放整个伊甸区的内存。由于伊甸区中幸存下来的可达对象并不多,复制开销不大,所以这里非常适合用复制算法。

③GC扫描线程也会扫描幸存区,然后把GC扫描到的可达对象通过复制算法拷贝到幸存区的另一半,然后释放掉幸存区原来那一半的内存。对于幸存区之间的拷贝,每一轮GC会拷贝多个对象、也会淘汰多个对象。

④当某个对象在幸存区中存活过很多轮GC扫描之后,JVM就认为这个对象在短时间内应该是不会成为垃圾的,就会把这个对象拷贝到老年代。

⑤进入老年代的对象也会被GC扫描,但老年代GC扫描的频率会比新生代GC扫描的频率低很多(这减少了GC扫描的开销)。老年代使用标记整理的方式对内存进行回收。

新生代使用复制算法进行垃圾回收,老年代使用标记整理进行垃圾回收。

分代回收是JVM中主要的垃圾回收思想方法。但是在垃圾回收器具体实现的时候,可能还会有一些调整和优化。

相关文章:

JVM类加载的过程和JVM垃圾回收机制

文章目录 一、JVM类加载的过程1.1类加载的基本流程1.1.1加载1.1.2验证1.1.3准备1.1.4解析1.1.5初始化 1.2双亲委派模型 二、JVM垃圾回收机制2.1找到垃圾2.1.1引用计数(比如Python,PHP中用到)2.1.2可达性分析(比如Java中用到) 2.2释放垃圾2.2.1标记清除2.2.2复制算法…...

【git error|SourceTree】error: bad signature 0x00000000 fatal: index file corrupt

报错 error: bad signature 0x00000000 fatal: index file corrupt 场景 在使用git add . 提交代码到缓冲区时或使用SourceTree时电脑宕机,重启后再次提交代码会出现该提示 原因分析 .git目录下的index文件损坏 解决方式 //删除索引文件 rm -f .git/index //回…...

读书笔记:《宽客人生:依曼纽尔·德曼》

金融工程,也叫数量金融,洞察了证券价值与不确定性之间的关系。 布莱克-斯科尔斯模型可以告诉我们如何利用标的股票来复制期权,以及复制期权的成本,做市商利用此来复制期权,以规避无法从其他人那里购买合适价格的期权的…...

车载通信架构 —— 传统车内通信网络LIN总线(低成本覆盖低速场景)

车载通信架构 —— 传统车内通信网络LIN总线(低成本覆盖低速场景) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是…...

单例模式与多线程

目录 前言 正文 1.立即加载/饿汉模式 2.延迟加载/懒汉模式 1.延迟加载/懒汉模式解析 2.延迟加载/懒汉模式的缺点 3.延迟加载/懒汉模式的解决方案 (1)声明 synchronized 关键字 (2)尝试同步代码块 (3&am…...

Kafka系列 - Kafka一篇入门

Kafka是一个分布式流式处理平台。很多分布式处理系统,例如Spark,Flink等都支持与Kafka集成。 Kafka使用场景 消息系统:Kafka实现了消息顺序性保证和回溯消费。存储系统:Kafka把消息持久化到磁盘,相比于其他基于内存的…...

百度 文心一言 sdk 试用

JMaven Central: com.baidu.aip:java-sdk (sonatype.com) Java sdk地址如上: 文心一言开发者 文心一言 (baidu.com) ERNIE Bot SDK https://yiyan.baidu.com/developer/doc#Fllzznonw ERNIE Bot SDK提供便捷易用的接口,可以调用文心一言的能力&#…...

SQLite 和 SQLiteDatabase 的使用

实验七:SQLite 和 SQLiteDatabase 的使用 7.1 实验目的 本次实验的目的是让大家熟悉 Android 中对数据库进行操作的相关的接口、类等。SQLiteDatabase 这个是在 android 中数据库操作使用最频繁的一个类。通过它可以实现数据库的创建或打开、创建表、插入数据、删…...

Dempster-Shafer(D-S)证据理论的基本定义和详细分析,优点,缺点,应用!!(系列1)

文章目录 前言一、D-S证据理论的应用:二、D-S证据理论的优点:三、D-S证据理论的缺陷:四、D-S组合规则:总结 前言 Dempster-Shafer(D-S)证据理论是一种不精确推理理论,也称为Dempster/Shafer证据…...

Leetcode—15.三数之和【中等】

2023每日刷题&#xff08;四十一&#xff09; Leetcode—15.三数之和 实现代码 class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> ans;int i, j, k;int s,…...

3、Qt使用windeploy工具打包可执行文件

新建一个文件夹&#xff0c;把要打包的可执行文件exe拷贝过来 点击输入框&#xff0c;复制一下文件夹路径 点击电脑左下角&#xff0c;找到Qt文件夹&#xff0c; 点击打开 “Qt 5.12.0 for Desktop” &#xff08;我安装的是Qt 5.12.0版本&#xff09; 输入“cd bin”&#xff…...

[DFS深度优先搜索]集合里的乘法

集合里的乘法 题目描述 给定一个目标数T和一个整数集合S&#xff0c;判断是否存在S的一个非空子集&#xff0c;子集中的数相乘的积为T。 关于输入 输入为两行。 第一行为目标数T&#xff0c;和S中的元素个数N&#xff0c;以空格隔开。 第二行为S中的N个元素&#xff0c;以空…...

K8s 中 Pod OOMKilled 原因

目录 Exit Code 137 解决方案 JVM 感知 cgroup 限制 使用 JDK9 的容器感知机制尝试 问题分析 容器内部感知 CGroup 资源限制 在 Java10 中&#xff0c;改进了容器集成 JVM 参数 MaxDirectMemorySize -XX:MaxDirectMemorySize 的默认值是什么&#xff1f; 其他获取 ma…...

为什么程序员最应该学习的是运营与销售,而不是技术?

大概几个月前&#xff0c;我加入了某副业交流群。这里人才很多&#xff0c;不光是传统意义上的程序员&#xff0c;也有公司老板、偏门大佬、产品经理等。 群里的聊天主题就是搞钱俩字&#xff0c;大家讨论着如何搞钱&#xff0c;分享每日收益情况&#xff0c;以及自己做的产品等…...

MySql数据库常用指令(五)多表连接

MySql数据库常用指令&#xff08;五&#xff09;多表连接 一、内连接,或等值连接二、左连接三、右连接 实际应用中&#xff0c;我们常常要连接几个不同的MySQL表&#xff0c;因此在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询 INNER JOIN&#xff08;内…...

Centos7使用rpm安装mysql 5.7.43

Centos7使用rpm安装mysql 5.7.43 1、下载rpm包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.43-1.el7.x86_64.rpm-bundle.tar2、解压并安装 tar xf mysql-5.7.43-1.el7.x86_64.rpm-bundle.tar yum -y install mysql-*3、按需修改mysql配置 #注意&a…...

补充:如何提高selenium的运行速度?

已经通读该专栏文章的同学,或许对UI自动化测试有了一定的掌握,细心的同学肯定会发现一个问题,当用例量达到一定程度时,对于整体用例的执行速度肯定不会很满意。除了应用多线程运行用例的方式加快速度,有没有其他的方法呢? 今天告诉大家,方法是有的!也是本人新学的。即…...

使用Python+Redis实现文章投票网站后端功能

1&#xff0e;实现投票功能&#xff0c;2&#xff0e;创建文章数据&#xff0c;3&#xff0e;对文章进行排序。 实现投票功能 实现投票功能&#xff0c;要注重文章的时效性与投票的公平性&#xff0c;所以需要给投票功能加上一些约束条件&#xff1a; 文章发布满一个星期后&…...

SpringBoot 环境使用 Redis + AOP + 自定义注解实现接口幂等性

目录 一、前言二、主流实现方案介绍2.1、前端按钮做加载状态限制&#xff08;必备&#xff09;2.2、客户端使用唯一标识符2.3、服务端通过检测请求参数进行幂等校验&#xff08;本文使用&#xff09; 三、代码实现3.1、POM3.2、application.yml3.3、Redis配置类3.4、自定义注解…...

Leetcode—18.四数之和【中等】

2023每日刷题&#xff08;四十一&#xff09; Leetcode—18.四数之和 实现代码 class Solution { public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ans;sort(nums.begin(), nums.end());int n …...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能

指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...