(待更新)📝JVM-2:JVM-垃圾回收
如何判断对象可被回收垃圾回收算法判断垃圾可被回收:引用计数法当前对象被一个变量所引用,则该对象引用计数加一当对象的引用计数为 0 时 -> 应该回收
缺陷:这种情况会导致内存泄漏
判断垃圾可被回收:可达性分析算法这是 JVM 判断对象是否为垃圾的算法
一些根对象(GC Root):一定不会被回收的对象JVM 进行垃圾回收时,会先去堆中将所有对象扫描一遍,看是否能够沿着 GC Root 为起点的引用链找到该对象:如果找不到 -> 表示可以回收;如果找得到 -> 不回收
阅读扩展:对象的存活与毁灭(待优化)要经历两次标记:
第一次标记:是否在 GC Root 的引用链上(是否可达)
(在对象被判断为不可达之后不会立即回收,还会有第二次标记)将第一次标记为需要回收的对象放入一个 F-Queue 队列中,然后让一个优先级较低的线程来调用队列中要回收的对象的 finalize()
第二次标记(对象的自救)
通过判断是否重写了 finalize() / 是否已经被调用过 finalize(),来决定是否还要调用该方法-> 只有未被调用过 finalize ...
📝Redis-原理篇-4:内存回收
1 Key Expiration (Key 过期)DB 结构
Redis 如何知道一个 key 过期?-> 一个库中有两个 Dict 分别记录 key-value 对、key-ttl 对
是不是 TTL 到期就立刻删除了?-> 不是,具体取决于过期策略
数据过期策略
惰性删除:
在访问一个 key 时检查它的存活时间,如果过期 -> 执行删除
定期删除:
通过一个定时任务周期性地抽样部分 key,删除其中过期的 key-> 能确保最终所有 Dict 中的数据都被抽到-> 不会出现过期 key 一直没清理的情况
定期删除分为两种执行模式:
SLOW 模式:低频、高时长的清理
Redis 会设置一个定时任务 serverCron(),按照 server.hz 的频率来执行过期 key 清理,模式为SLOW
SLOW 模式规则:
执行频率受 server.hz 影响,默认为 10(即每秒执行 10 次,每个执行周期 100 ms)
执行清理的耗时不超过一次执行周期的 25%
逐个遍历 db,逐个遍历 db 中的 bucket,抽取 20 个 key ...
📝Redis-原理篇-3:通信协议
Redis 采用 C/S 架构通信一般分为两部(不包括 pipeline 和 pubsub):
client 向 server 发送一条命令
server 解析并执行命令,返回响应结果给 client
Redis 采用 RESP(Redis Serialization Protocol) 协议
📝Redis-原理篇-2:网络模型
五种 I/O 模型用户空间和内核空间在操作系统中 CPU 有两种运行状态:用户态(User Mode)、内核态(Kernel Mode)。当进程运行在内核空间时称为内核态,当进程运行在用户空间时称为用户态。应用程序运行在用户态,一些操作系统的程序运行在内核态。用户态下只能执行一些非特权指令,如果应用程序需要执行一些特权指令(如磁盘的 IO、网络通信等),必须通过中断机制从用户态切换到内核态,并将处理权交给操作系统,由操作系统来完成特权指令。为此,操作系统向用户空间提供了一组标准接口,即系统调用(System Call),这是用户程序访问内核服务的唯一通道。
Linux 的五种 IO 模型
BIO = Biocking IO (阻塞 IO)
NIO = Nonblocking (非阻塞 IO)
IO Multiplexing (IO 多路复用)
Signal Driven IO (信号驱动 IO)
AIO = Asynchronous IO (异步 IO)
应用程序想要读取数据,是无法直接读取磁盘数据的,需要先在内核中等待内核操作硬件拿到数据。
...
(待更新)📝JVM-1:JVM 简介 & 运行时数据区
JVM 简介Java 程序的运行环境(Java 二进制字节码的运行环境)
好处:
一次编写,到处运行
自动内存管理,垃圾回收功能
数组下标越界检查
多态的基石
运行时数据区PC Register(程序计数器)PC Register,Program Counter Register,程序计数器
Java 中代码的执行流程Java 代码首先被编译成字节码(JVM 指令),然后这些字节码交由 JVM 执行引擎的解释器进行解释。解释器将字节码转换为机器码,最终交由 CPU 执行。
程序计数器的作用负责记住下一条 JVM 指令的执行地址,从而保证程序执行的有序性
负责记录下一条 JVM 指令的执行地址,从而确保程序执行的有序性。
在多线程环境中,每个线程都有自己的程序计数器,记录该线程上次执行结束的位置。当线程被调度时,程序计数器指示从上次停止的位置继续执行。
程序计数器的实现程序计数器是通过“寄存器”实现的。寄存器是 CPU 中访问速度最快的存储单元,Java 将寄存器作为程序计数器来存储和读取指令的内存地址,因为指令的读取频率很高。
程序计数器的特点
线程私有
唯一一个不会存在内存 ...
📝Redis-高级篇-1-4:分布式缓存-分片集群
1 概述主从和哨兵可以解决 Redis 高可用、高并发读的问题。但是仍有两个问题没有解决:
海量数据存储
高并发写
使用分片集群可以解决上述问题,分片集群的特征是:
集群中有多个 master,每个 master 保存不同数据。
每个 master 都可以有多个 slave 节点
master 之间通过 ping 监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
2 hash slot (散列插槽)Redis 会把每一个 master 节点映射到 0~16383 共 16384 个插槽上
数据 key 不是与节点绑定,而是与插槽绑定。Redis 会根据 key 的有效部分计算插槽值,分两种情况:
key 中包含 “{}”,且 “{}” 中至少包含 1 个字符,”{}” 中的部分是有效部分
key 中不包含 “{}”,整个 key 都是有效部分
例如:key 是 num,那么就根据 num 计算,如果是 {hello}num,则根据 hello 计算。计算方式是利用 CRC16 算法得到一个 hash 值,然后对 16384 取余,得到的结果就是 s ...
📝Redis-高级篇-1-3:分布式缓存-哨兵机制
1 哨兵的作用和原理哨兵,Sentinel,作用是实现主从集群的自动故障恢复。
哨兵的结构和作用
监控:Sentinel 会不断检查 master 和 slave 是否按预期工作
自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master。当故障实例恢复后也以新的 master 为主
通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给 Redis 的客户端
服务状态监控Sentinel 基于心跳机制监测服务状态,每隔 1 秒向集群的每个实例发送 ping 命令:
主观下线:如果 sentinel 节点发现某实例未在规定时间响应,则认为该实例主管下线。
客观下线:如果超过指定数量(quorum)的 sentinel 都认为该实例主观下线,则该实例客观下线,quorum 值最好超过 Sentinel 实例数量的一半。
选举新的 master一旦发现 master 故障,sentinel 需要在 slave 中选择一个作为新的 master,选择依据是这样的:
首先判断 slave 节点与 ...
📝Redis-高级篇-1-2:分布式缓存-主从集群
1 主从集群结构单节点 Redis 的并发能力是有上限的,要进一步提高 Redis 的并发能力就需要搭建主从集群,实现读写分离。
搭建流程(TODO:补充)
2 主从数据同步原理两个重要概念master 判断 slave(replica 同义) 是不是第一次同步数据的逻辑涉及到两个重要概念:
Replication Id:简称 replid,是数据集的标记,id 一致则说明是同一个数据集。每一个 master 都有唯一的 replid,slave 则会继承 master 节点的 replid。
offset:偏移量,随着记录在 repl_baklog 中的数据增多而逐渐增大。slave 完成同步时也会记录当前同步的 offset。如果 slave 的 offset < master 的 offset,则说明 slave 数据落后于 master,需要更新。(如果相等则说明当前是数据一致的。)
因此,slave 做数据同步时,必须向 master 声明自己的 replication id 和 offset,master 才可以判断到底需要同步哪些数据。
全量同步
全量同步的流 ...
📝Redis-高级篇-1-1:分布式缓存-持久化机制
1 RDB (数据快照)概念RDB(Redis Data Backup file) = Redis 数据备份文件 = Redis 数据快照
把内存中的所有数据都记录到磁盘中。Redis 实例故障重启后,从磁盘读取快照文件恢复数据。
.rdb 文件默认保存在当前运行目录。
save 命令 和 bgsave 命令
save:由 Redis 主进程来执行 RDB,会阻塞所有命令 完成后返回 “ok”
bgsave:开启子进程异步执行 RDB,避免主进程受到影响 会立刻返回 “background saving started”
配置Redis 会默认在服务停止时触发 RDB,但是也可以在 redis.conf 中配置,格式如下:
123456# 900 秒内如果至少有 1 个 key 被修改,就执行 bgsavesave 900 1save 300 10save 60 10000# save "" 表示禁用 RDB# save ""
其他常用配置:
12345678# 是否压缩,建议不开启,因为也会消耗 CPU,而磁盘相对很 ...
📝Redis-原理篇-1:数据结构
1 Simple Dynamic String (简单动态字符串)因为 C 语言的字符串存在很多问题:
获取字符串长度需要遍历,效率低
非二进制安全(无法处理 ‘\0’ 等特殊字节)
不支持动态扩容
所以 Redis 没有直接使用 C 语言的字符串,而是构建了一种新的字符串结构:SDS(Simple Dynamic Strings),简单动态字符串
SDS 的结构SDS 本质上是一个结构体, char[] 由 SDS 自己来管理
根据字符串长度的不同,SDS 提供多种结构体实现来支持不同大小的字符串(如下图中为 sdsdr8),以节省内存空间
动态扩容——内存预分配当向 SDS 追加字符串超出当前容量时,会自动申请更大的内存:
新字符串长度 < 1MB:新容量 = 扩展后长度 x 2 + 1
新字符串长度 >= 1MB:新容量 = 扩展后长度 + 1MB + 1(每次固定增长 1MB,防止几何增长造成浪费)
结构就分为这两部分:
Header:记录当前字符串的长度等信息,便于读取
buf[]:真实存储字符串的 char[]
SD ...