JVM类加载器详解
回顾一下类加载过程在开始介绍类加载器和双亲委派机制之前,先简单回顾一下类加载过程
类加载过程:加载->连接->初始化。
连接过程又可分为三步:验证->准备->解析。
加载是类加载过程的第一步,主要完成下面 3 件事情:
通过全类名获取定义此类的二进制字节流
将字节流所代表的静态存储结构转换为方法区的运行时数据结构
在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
类加载器类加载器的概念:
类加载器是一个负责加载类的对象,用于实现类加载过程中的加载这一步。
每个 Java 类都有一个引用指向加载它的 ClassLoader。
数组类不是通过 ClassLoader 创建的(数组类没有对应的二进制字节流),是由 JVM 直接生成的。
简而言之,类加载器的主要作用就是加载 Java 类的字节码(. class)文件到 JVM 中(在内存中生成一个代表该类的 class 对象)。
类加载器加载规则JVM 启动的时候,并不会一次性加载所有的类,而是根据需要去动态加载。也就是说,大部分类在具体用到的时候才会去加载,这样对内存更加友好。 ...
JVM类加载过程详解
类的生命周期类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段::加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)。
类加载过程Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机是如何加载这些 Class 文件呢?
系统加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。
加载类加载过程的第一步,主要完成下面 3 件事情:
通过全类名获取定义此类的二进制字节流。
将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。
加载这一步主要是通过我们后面要讲到的 类加载器 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 双亲委 ...
JVM垃圾回收详解
堆空间的基本结构Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是堆内存中对象的分配与回收。
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。
从垃圾回收的角度来说,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆被划分为了几个不同的区域,这样我们就可以根据各个区域的特点选择合适的垃圾收集算法。
JDK堆内存被通常分为下面三部分:
新生代(Young Generation)
老年代(Old Generation)
元空间(Metaspace)
下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。
JVM 一次完整的 GC 流程是什么
首先,任何新对象都分配到 eden 空间。两个幸存者空间开始时都是空的。
当 eden 空间填满时,将触发一个 Minor GC (年轻代的垃圾回收,也称为 Young GC),删除所有未引用的对象;大对象(需要大量连续内存空间的 Java 对象 ...
虚拟机创建对象流程
Step1:类加载检查虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
Step2:分配内存在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
内存分配的两种方式 :
指针碰撞:
适用场合:堆内存规整(即没有内存碎片)的情况下。
原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。
使用该分配方式的 GC 收集器:Serial, ParNew
空闲列表:
适用场合:堆内存不规整的情况下。
原理:虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找 ...
JVM 运行时数据区详解
JVM 运行时数据区详解JVM 主要由三个子系统构成,分别是:类加载子系统、运行时数据区(内存结构)和 字节码执行引擎。
JVM 的体系结构全貌:
当我们开发 Java 程序时,首先会编写 .java 文件,之后,会将 .java 文件编译成 .class 文件。
在 JVM 中,会通过类加载子系统将 .class 文件的内容装载到 JVM 的运行时数据区,而 JVM 的运行时数据区又会分为:方法区、堆、栈、本地方法栈和程序计数器几个部分。
在装载 class 文件的内容时,会将 class 文件的内容拆分为几个部分,分别装载到 JVM 运行时数据区的几个部分。
方法区也叫作元空间,主要包含了:运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应的 Class 实例的引用等信息。
在 JVM 中,程序的执行是通过执行引擎进行的,执行引擎会调用本地方法的接口来执行本地方法库,进而完成整个程序逻辑的执行。
我们常说的垃圾收集器是包含在执行引擎中的,在程序的运行过程中,执行引擎会开启垃圾收集器,并在后台运行,垃圾收集器会不断监控程序运行过程中产生的内存垃圾信息,并根据相应的策 ...
布隆过滤器
布隆过滤器主要是为了解决海量数据的存在性问题。对于海量数据中判定某个数据是否存在且容忍轻微误差这一场景(比如缓存穿透、海量数据去重)来说,非常适合。
什么是布隆过滤器布隆过滤器可以看作是由二进制向量(位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
Bloom Filter 会使用一个较大的 bit 数组来保存所有的数据,数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1(代表 false 或者 true),这也是 Bloom Filter 节省内存的核心所在。这样来算的话,申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 KB ≈ 122KB 的空间。
总而言之,布隆过滤器是一种用来检索元素是否在给定大集合中的数据结 ...
MySQL索引
MySQL 索引什么是索引索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。
索引底层结构选型Hash 表哈希表是键值对的集合,通过键(key)即可快速取出对应的值(value),因此哈希表可以快速检索数据(接近 O(1))。
为何能够通过 key 快速取出 value 呢? 原因在于 哈希算法(也叫散列算法)。通过哈希算法,我们可以快速找到 key 对应的 index,找到了 index 也就找到了对应的 value。
既然哈希表这么快,为什么 MySQL 没有使用其作为索引的数据结构呢?
主要是因为 Hash 索引不支持顺序和范围查询。假如我们要对表中的数据进行排序或者进行范围查询,那 Hash 索引可就不行了。并且,每次 IO 只能取一个。
1SELECT * FROM tb1 WHERE id < 500;
在这种范围查询中,优势非常大,直接遍历比 500 小的叶子节点就够了。而 Hash 索引是根据 hash 算法来定位的,难不成还要把 1 - 499 的数据,每个都进行一次 hash 计算来定位吗?这就是 Hash 最大的缺点了。 ...
MySQL事务
数据库事务概念一句话,要么全部执行成功,要么全部不执行 。
而关系型数据库都有 ACID 特性:
原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
1只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!
并发事务带来了哪些问题MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。
那么在同时处理多个事务的时候,就可能出现脏读、不可重复读、幻读的问题。
脏读(Dirty Read)如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读 ...
Mybatis二级缓存是什么
二级缓存什么是二级缓存MyBatis 中的二级缓存是指作用于映射器(Mapper)范围的缓存,也就是跨多个 SqlSession 的会话缓存。当开启二级缓存后,MyBatis 将会使用一个全局的缓存来存储查询结果,这个缓存的生命周期更长,可以跨越多个 SqlSession。
二级缓存的作用MyBatis 的二级缓存主要作用是减少数据库查询次数,提高数据访问的效率。它是跨 SqlSession 的,在 Mapper 级别共享,这意味着当不同的 SqlSession 执行相同的查询时,可以重用之前查询的结果,而不需要再次执行数据库操作。
提高性能:二级缓存可以大幅减少对数据库的查询次数,特别是对于那些不经常变化的数据,通过缓存可以快速获取结果,从而加快应用程序的响应速度。
减少数据库负载:通过使用缓存,减少了对数据库的直接访问,这可以在高并发场景下显著降低数据库的压力,保护数据库不被过度使用。
跨会话共享:不同用户或请求的相同数据可以被缓存在二级缓存中,实现跨会话共享,这对于读多写少的应用场景尤其有益。
支持分布式:通过集成第三方缓存管理器(如 Ehcache、Redis 等),M ...
SQL语句的执行流程
一条 SQL 执行语句如下
1select * from book where id = 1;
那么,在执行一条这样的语句时,其内部都发生了什么?
MySQL 的执行流程下图展示了一条 SQL 语句的执行流程:
可以看到, MySQL 的架构共分为两层:Server 层和存储引擎层
其中,
Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现。
存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树,且是默认使用,也 ...
