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每日刷题(四十一) 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工具打包可执行文件
新建一个文件夹,把要打包的可执行文件exe拷贝过来 点击输入框,复制一下文件夹路径 点击电脑左下角,找到Qt文件夹, 点击打开 “Qt 5.12.0 for Desktop” (我安装的是Qt 5.12.0版本) 输入“cd bin”ÿ…...
[DFS深度优先搜索]集合里的乘法
集合里的乘法 题目描述 给定一个目标数T和一个整数集合S,判断是否存在S的一个非空子集,子集中的数相乘的积为T。 关于输入 输入为两行。 第一行为目标数T,和S中的元素个数N,以空格隔开。 第二行为S中的N个元素,以空…...

K8s 中 Pod OOMKilled 原因
目录 Exit Code 137 解决方案 JVM 感知 cgroup 限制 使用 JDK9 的容器感知机制尝试 问题分析 容器内部感知 CGroup 资源限制 在 Java10 中,改进了容器集成 JVM 参数 MaxDirectMemorySize -XX:MaxDirectMemorySize 的默认值是什么? 其他获取 ma…...
为什么程序员最应该学习的是运营与销售,而不是技术?
大概几个月前,我加入了某副业交流群。这里人才很多,不光是传统意义上的程序员,也有公司老板、偏门大佬、产品经理等。 群里的聊天主题就是搞钱俩字,大家讨论着如何搞钱,分享每日收益情况,以及自己做的产品等…...
MySql数据库常用指令(五)多表连接
MySql数据库常用指令(五)多表连接 一、内连接,或等值连接二、左连接三、右连接 实际应用中,我们常常要连接几个不同的MySQL表,因此在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询 INNER JOIN(内…...

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.实现投票功能,2.创建文章数据,3.对文章进行排序。 实现投票功能 实现投票功能,要注重文章的时效性与投票的公平性,所以需要给投票功能加上一些约束条件: 文章发布满一个星期后&…...

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

Leetcode—18.四数之和【中等】
2023每日刷题(四十一) 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 …...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学
一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件,其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时,价带电子受激发跃迁至导带,形成电子-空穴对,导致材料电导率显著提升。…...
FTXUI::Dom 模块
DOM 模块定义了分层的 FTXUI::Element 树,可用于构建复杂的终端界面,支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...
项目研究:使用 LangGraph 构建智能客服代理
概述 本教程展示了如何使用 LangGraph 构建一个智能客服代理。LangGraph 是一个强大的工具,可用于构建复杂的语言模型工作流。该代理可以自动分类用户问题、分析情绪,并根据需要生成回应或升级处理。 背景动机 在当今节奏飞快的商业环境中,…...

NoSQL——Redis配置与优化
目录 关系型&非关系型数据库 一、核心原理对比 二、核心特性对比 三、关键区别剖析 四、典型产品示例 总结 Redis Redis核心原理 核心特性 技术意义 配置文件解析 1. 基础配置 2. 持久化配置 3. 内存管理 4. 高可用配置 5. 性能调优 6.…...