Skip to content

Commit 790c625

Browse files
committed
Update Java Notes
1 parent 46e3714 commit 790c625

File tree

5 files changed

+98
-104
lines changed

5 files changed

+98
-104
lines changed

DB.md

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2550,7 +2550,7 @@ Buffer Pool 是一片内存空间,可以通过 innodb_buffer_pool_size 来控
25502550

25512551
* Change Buffer 是 Buffer Pool 里的内存,不能无限增大,用来对增删改操作提供缓存
25522552
* Change Buffer 的大小可以通过参数 innodb_change_buffer_max_size 来动态设置,设置为 50 时表示 Change Buffer 的大小最多只能占用 Buffer Pool 的 50%
2553-
* 补充知识:唯一索引的更新不能使用 Buffer,一般只有普通索引可以使用,直接写入 Buffer 就结束
2553+
* 补充知识:**唯一索引的更新不能使用 Buffer**,一般只有普通索引可以使用,直接写入 Buffer 就结束
25542554

25552555
InnoDB 的数据是按数据页为单位来读写,每个数据页的大小默认是 16KB。数据是存放在磁盘中,每次读写数据都需要磁盘 IO,效率会很低。InnoDB 提供了缓存 Change Buffer,Buffer 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:
25562556

@@ -5567,9 +5567,10 @@ MySQL 4.1 版本之后,开始支持 SQL 的子查询
55675567
* 优化方式二:方案适用于主键自增的表,可以把 LIMIT 查询转换成某个位置的查询
55685568

55695569
```mysql
5570-
EXPLAIN SELECT * FROM tb_user_1 WHERE id > 200000 LIMIT 10;
5570+
EXPLAIN SELECT * FROM tb_user_1 WHERE id > 200000 LIMIT 10; -- 写法 1
5571+
EXPLAIN SELECT * FROM tb_user_1 WHERE id BETWEEN 200000 and 200010; -- 写法 2
55715572
```
5572-
5573+
55735574
![](https://gitee.com/seazean/images/raw/master/DB/MySQL-优化SQL分页查询3.png)
55745575

55755576

@@ -9391,6 +9392,8 @@ sorted_set类型:在 set 的存储结构基础上添加可排序字段,类
93919392
- 有序集合保存的元素个数要小于 128 个;
93929393
- 有序集合保存的所有元素大小都小于 64 字节
93939394

9395+
当元素比较多时,此时 ziplist 的读写效率会下降,时间复杂度是 O(n),跳表的时间复杂度是 O(logn)
9396+
93949397

93959398

93969399
***
@@ -10034,7 +10037,7 @@ AOF 和 RDB 同时开启,系统默认取 AOF 的数据(数据不会存在丢
1003410037
1003510038
- 对数据**非常敏感**,建议使用默认的 AOF 持久化方案
1003610039
10037-
AOF 持久化策略使用 everysecond,每秒钟 fsync 一次,该策略 redis 仍可以保持很好的处理性能,当出现问题时,最多丢失 1 秒内的数据
10040+
AOF 持久化策略使用 everysecond,每秒钟 fsync 一次,该策略 redis 仍可以保持很好的处理性能,当出现问题时,最多丢失 1 秒内的数据
1003810041
1003910042
注意:AOF 文件存储体积较大,恢复速度较慢,因为要执行每条指令
1004010043
@@ -10638,7 +10641,7 @@ Redis 如果不设置最大内存大小或者设置最大内存大小为 0,在
1063810641

1063910642
* 容量瓶颈:内存不足,放弃使用 redis
1064010643

10641-
* 解决方案:为了避免单点 Redis 服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上连接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份
10644+
* 解决方案:为了避免单点 Redis 服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上连接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现 Redis 高可用,同时实现数据冗余备份
1064210645

1064310646
<img src="https://gitee.com/seazean/images/raw/master/DB/Redis-主从复制多台服务器连接方案.png" style="zoom: 80%;" />
1064410647

@@ -11580,32 +11583,15 @@ Read-Through Pattern 也存在首次不命中的问题,采用缓存预热解
1158011583

1158111584
解决方案:
1158211585

11583-
* 思路:
11584-
11585-
1. 更多的页面静态化处理
11586-
11587-
2. 构建**多级缓存**架构:Nginx 缓存 + Redis 缓存 + ehcache 缓存
11588-
11589-
3. 检测 MySQL 严重耗时业务进行优化:对数据库的瓶颈排查,例如超时查询、耗时较高事务等
11590-
11591-
4. 灾难预警机制:监控 Redis 服务器性能指标,CPU 使用率、内存容量、平均响应时间、线程数
11592-
11593-
5. 限流、降级:短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问
11594-
11595-
* 实践:
11596-
11597-
1. LRU 与 LFU切换
11598-
11599-
2. 数据有效期策略调整:根据业务数据有效期进行分类错峰,A 类 90 分钟,B 类 80 分钟,C 类 70 分钟,过期时间使用固定时间 + 随机值的形式,稀释集中到期的 key 的数量
11600-
11601-
3. 超热数据使用永久 key
11602-
11603-
4. 定期维护:对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时
11604-
11605-
5. 加锁:慎用
11586+
1. 加锁,慎用
11587+
2. 设置热点数据永远不过期,如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中
11588+
3. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
11589+
4. 构建**多级缓存**架构,Nginx 缓存 + Redis 缓存 + ehcache 缓存
11590+
5. 灾难预警机制,监控 Redis 服务器性能指标,CPU 使用率、内存容量、平均响应时间、线程数
11591+
6. 限流、降级:短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问
1160611592

1160711593

11608-
总的来说:缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
11594+
总的来说:缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约 40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
1160911595

1161011596

1161111597

@@ -11639,7 +11625,7 @@ Read-Through Pattern 也存在首次不命中的问题,采用缓存预热解
1163911625

1164011626
5. 加锁:分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重
1164111627

11642-
总的来说:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中 redis 后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个 key 的过期监控难度较高,配合雪崩处理策略即可
11628+
总的来说:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中 Redis 后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个 key 的过期监控难度较高,配合雪崩处理策略即可
1164311629

1164411630

1164511631

@@ -11667,14 +11653,14 @@ Read-Through Pattern 也存在首次不命中的问题,采用缓存预热解
1166711653

1166811654
1. 缓存 null:对查询结果为 null 的数据进行缓存,设定短时限,例如 30-60 秒,最高 5 分钟
1166911655

11670-
2. 白名单策略:提前预热各种分类数据 id 对应的 **bitmaps**,id 作为 bitmaps 的 offset,相当于设置了数据白名单。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低),也可以使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
11656+
2. 白名单策略:提前预热各种分类**数据 id 对应的 bitmaps**,id 作为 bitmaps 的 offset,相当于设置了数据白名单。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低),也可以使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
1167111657

1167211658
3. 实时监控:实时监控 Redis 命中率(业务正常范围时,通常会有一个波动值)与 null 数据的占比
1167311659

1167411660
* 非活动时段波动:通常检测 3-5 倍,超过 5 倍纳入重点排查对象
1167511661
* 活动时段波动:通常检测10-50 倍,超过 50 倍纳入重点排查对象
1167611662

11677-
根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)
11663+
根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控
1167811664

1167911665
4. key 加密:临时启动防灾业务 key,对 key 进行业务层传输加密服务,设定校验程序,过来的 key 校验;例如每天随机分配 60 个加密串,挑选 2 到 3 个,混淆到页面数据 id 中,发现访问 key 不满足规则,驳回数据访问
1168011666

Java.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4915,7 +4915,7 @@ HashMap继承关系如下图所示:
49154915
}
49164916
```
49174917

4918-
* 构造一个具有指定的初始容量和负载因子的HashMap
4918+
* 构造一个具有指定的初始容量和负载因子的 HashMap
49194919

49204920
```java
49214921
public HashMap(int initialCapacity, float loadFactor) {
@@ -4942,7 +4942,7 @@ HashMap继承关系如下图所示:
49424942
}
49434943
```
49444944

4945-
putMapEntries源码分析
4945+
putMapEntries 源码分析
49464946

49474947
```java
49484948
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
@@ -4987,8 +4987,8 @@ HashMap继承关系如下图所示:
49874987

49884988
* hash():HashMap 是支持 Key 为空的;HashTable 是直接用 Key 来获取 HashCode,key 为空会抛异常
49894989

4990-
* &(按位与运算):相同的二进制数位上,都是1的时候,结果为1,否则为零
4991-
* ^(按位异或运算):相同的二进制数位上,数字相同,结果为0,不同为1,**不进位加法**
4990+
* &(按位与运算):相同的二进制数位上,都是 1 的时候,结果为 1,否则为零
4991+
* ^(按位异或运算):相同的二进制数位上,数字相同,结果为 0,不同为 1,**不进位加法**
49924992

49934993
```java
49944994
static final int hash(Object key) {
@@ -5185,9 +5185,9 @@ HashMap继承关系如下图所示:
51855185

51865186
HashMap 在进行扩容后,节点**要么就在原来的位置,要么就被分配到"原位置+旧容量"的位置**
51875187

5188-
判断:e.hash 与 oldCap 对应的有效高位上的值是 1,即当前数组长度 n 1 的位为 x,如果 key 的哈希值 x 位也为 1,则扩容后的索引为 now + n
5188+
判断:e.hash 与 oldCap 对应的有效高位上的值是 1,即当前数组长度 n 二进制为 1 的位为 x 位,如果 key 的哈希值 x 位也为 1,则扩容后的索引为 now + n
51895189

5190-
注意:这里也要求**数组长度 2 的幂**
5190+
注意:这里要求**数组长度 2 的幂**
51915191

51925192
![](https://gitee.com/seazean/images/raw/master/Java/HashMap-resize扩容.png)
51935193

@@ -10614,7 +10614,7 @@ objD.fieldG = G; // 写
1061410614

1061510615
* **写屏障 (Store Barrier) + SATB**:当原来成员变量的引用发生变化之前,记录下原来的引用对象
1061610616

10617-
保留 GC 开始时的对象图,即原始快照 SATB,当 GC Roots 确定后,对象图就已经确定,那后续的标记也应该是按照这个时刻的对象图走,如果期间对白色对象有了新的引用会记录下来,并且将白色对象变灰(说明可达了),重新扫描该对象
10617+
保留 GC 开始时的对象图,即原始快照 SATB,当 GC Roots 确定后,对象图就已经确定,那后续的标记也应该是按照这个时刻的对象图走,如果期间对白色对象有了新的引用会记录下来,并且将白色对象变灰(说明可达了),重新扫描该对象的引用关系
1061810618

1061910619
SATB (Snapshot At The Beginning) 破坏了条件一,从而保证了不会漏标
1062010620

@@ -11046,7 +11046,7 @@ G1(Garbage-First)是一款面向服务端应用的垃圾收集器,**应用
1104611046

1104711047
G1 对比其他处理器的优点:
1104811048

11049-
* **并发与并行**
11049+
* 并发与并行:
1105011050
* 并行性:G1 在回收期间,可以有多个 GC 线程同时工作,有效利用多核计算能力,此时用户线程 STW
1105111051
* 并发性:G1 拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此不会在整个回收阶段发生完全阻塞应用程序的情况
1105211052
* 其他的垃圾收集器使用内置的 JVM 线程执行 GC 的多线程操作,而 G1 GC 可以采用应用线程承担后台运行的 GC 工作,JVM 的 GC 线程处理速度慢时,系统会**调用应用程序线程加速垃圾回收**过程
@@ -11062,7 +11062,7 @@ G1 对比其他处理器的优点:
1106211062

1106311063
![](https://gitee.com/seazean/images/raw/master/Java/JVM-G1-Region区域.png)
1106411064

11065-
- **空间整合**
11065+
- 空间整合:
1106611066

1106711067
- CMS:标记-清除算法、内存碎片、若干次 GC 后进行一次碎片整理
1106811068
- G1:整体来看是基于标记 - 整理算法实现的收集器,从局部(Region 之间)上来看是基于复制算法实现的,两种算法都可以避免内存碎片
@@ -11146,7 +11146,7 @@ G1 中提供了三种垃圾回收模式:YoungGC、Mixed GC 和 FullGC,在不
1114611146
* 初始标记:标记从根节点直接可达的对象,这个阶段是 STW 的,并且会触发一次年轻代 GC
1114711147
* 根区域扫描 (Root Region Scanning):扫描 Survivor 区中指向老年代的,被初始标记标记了的引用及引用的对象,这一个过程是并发进行的,但是必须在 Young GC 之前完成
1114811148
* 并发标记 (Concurrent Marking):在整个堆中进行并发标记(应用程序并发执行),可能被 YoungGC 中断。会计算每个区域的对象活性,即区域中存活对象的比例,若区域中的所有对象都是垃圾,则这个区域会被立即回收(实时回收),给浮动垃圾准备出更多的空间,把需要收集的 Region 放入 CSet 当中
11149-
* 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中,这阶段需要停顿线程,但是可并行执行(防止漏标)
11149+
* 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中,这阶段需要停顿线程,但是可并行执行(**防止漏标**
1115011150
* 筛选回收:并发清理阶段,首先对 CSet 中各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率
1115111151

1115211152
![](https://gitee.com/seazean/images/raw/master/Java/JVM-G1收集器.jpg)
@@ -14875,7 +14875,7 @@ option 参数:
1487514875

1487614876
#### jhat
1487714877

14878-
jhat(JVM Heap Analysis Tool):Sun JDK 提供的 jhat 命令与 jmap 命令搭配使用,用于分析 jmap 生成的 heap dump 文件(堆转储快照),jhat 内置了一个微型的 HTTP/HTML 服务器,生成 dump 文件的分析结果后,用户可以在浏览器中查看分析结果
14878+
jhat(JVM Heap Analysis Tool):Sun JDK 提供的 jhat 命令与 jmap 命令搭配使用,用于**分析 jmap 生成的 heap dump 文件**(堆转储快照),jhat 内置了一个微型的 HTTP/HTML 服务器,生成 dump 文件的分析结果后,用户可以在浏览器中查看分析结果
1487914879

1488014880
使用语法:`jhat <options> <dumpfile>`
1488114881

@@ -14930,7 +14930,7 @@ options 参数:
1493014930

1493114931
- 对象等待中:Object.wait() 或 TIMED_WAITING
1493214932

14933-
- 停止:Parked
14933+
- 停止:Parked
1493414934

1493514935

1493614936

@@ -14986,6 +14986,8 @@ jstatd 是一个 RMI 服务端程序,相当于代理服务器,建立本地
1498614986

1498714987
工具的使用此处不再多言,推荐一个写的非常好的文章,JVM 调优部分的笔记全部参考此文章编写
1498814988

14989+
14990+
1498914991
视频链接:https://www.bilibili.com/video/BV1PJ411n7xZ?p=304
1499014992

1499114993
文章链接:https://www.yuque.com/u21195183/jvm/lv1zot
@@ -16515,7 +16517,7 @@ public class Kmp {
1651516517

1651616518
平衡二叉树(AVL)的特点:
1651716519

16518-
+ 二叉树左右两个子树的高度差不超过1
16520+
+ 二叉树左右两个子树的高度差不超过 1
1651916521
+ 任意节点的左右两个子树都是一颗平衡二叉树
1652016522

1652116523
平衡二叉树旋转:

Prog.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@ public class SpinLock {
12631263

12641264
##### 锁消除
12651265

1266-
锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除
1266+
锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除,这是 JVM **即时编译器的优化**
12671267

12681268
锁消除主要是通过**逃逸分析**来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除(同步消除:JVM 内存分配)
12691269

@@ -4086,7 +4086,7 @@ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
40864086
// 数量 + 1
40874087
int sz = ++size;
40884088

4089-
// 【做一次启发式清理】,如果没有清除任何 entry 并且当前使用量达到了负载因子所定义,那么进行 rehash
4089+
// 【做一次启发式清理】,如果没有清除任何 entry 并且【当前使用量达到了负载因子所定义,那么进行 rehash
40904090
if (!cleanSomeSlots(i, sz) && sz >= threshold)
40914091
// 扩容
40924092
rehash();
@@ -5898,8 +5898,6 @@ ThreadPoolExecutor 使用 int 的**高 3 位来表示线程池状态,低 29
58985898
}
58995899
```
59005900

5901-
5902-
59035901
* 拒绝策略相关的内部类
59045902

59055903

@@ -6238,7 +6236,7 @@ ThreadPoolExecutor 使用 int 的**高 3 位来表示线程池状态,低 29
62386236
// 如果线程数量是否超过最大线程数,直接回收
62396237
// 如果当前线程【允许超时回收并且已经超时了】,就应该被回收了,由于【担保机制】还要做判断:
62406238
// wc > 1 说明线程池还用其他线程,当前线程可以直接回收
6241-
// workQueue.isEmpty() 前置条件是 wc = 1,如果当前任务队列也是空了,最后一个线程就可以安全的退出
6239+
// workQueue.isEmpty() 前置条件是 wc = 1,如果当前任务队列也是空了,最后一个线程就可以退出】
62426240
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
62436241
// 使用 CAS 机制将 ctl 值 -1 ,减 1 成功的线程,返回 null,代表可以退出
62446242
if (compareAndDecrementWorkerCount(c))
@@ -6264,7 +6262,7 @@ ThreadPoolExecutor 使用 int 的**高 3 位来表示线程池状态,低 29
62646262
}
62656263
```
62666264

6267-
* processWorkerExit():**线程退出线程池**
6265+
* processWorkerExit():**线程退出线程池**,也有担保机制,保证队列中的任务被执行
62686266

62696267
```java
62706268
// 正常退出 completedAbruptly = false,异常退出为 true
@@ -13813,6 +13811,8 @@ TCP 协议的使用场景:文件上传和下载、邮件发送和接收、远
1381313811

1381413812
<img src="https://gitee.com/seazean/images/raw/master/Java/四次挥手.png" alt="四次挥手" style="zoom: 67%;" />
1381513813

13814+
推荐阅读:https://yuanrengu.com/2020/77eef79f.html
13815+
1381613816

1381713817

1381813818
***
@@ -13850,6 +13850,8 @@ ServerSocket 类:
1385013850
* 构造方法:`public ServerSocket(int port)`
1385113851
* 常用API:`public Socket accept()`,**阻塞等待**接收一个客户端的 Socket 管道连接请求,连接成功返回一个 Socket 对象
1385213852

13853+
三次握手后 TCP 连接建立成功,服务器内核会把连接从 SYN 半连接队列中移出,移入 accept (全连接)队列,等待进程调用 accept 函数时把连接取出。如果进程不能及时调用 accept 函数,就会造成 accept 队列溢出,最终导致建立好的 TCP 连接被丢弃
13854+
1385313855
相当于客户端和服务器建立一个数据管道,管道一般不用 close
1385413856

1385513857

@@ -14531,6 +14533,8 @@ NIO 使用的 SocketChannel 也是使用的堆外内存,源码解析:
1453114533
}
1453214534
```
1453314535

14536+
* 读操作相同
14537+
1453414538

1453514539

1453614540
***

0 commit comments

Comments
 (0)