Skip to content

Commit 1970470

Browse files
committed
Update Java Notes
1 parent 5246095 commit 1970470

File tree

5 files changed

+178
-162
lines changed

5 files changed

+178
-162
lines changed

DB.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5455,7 +5455,7 @@ MySQL Server 是多线程结构,包括后台线程和客户服务线程。多
54555455

54565456
复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步
54575457

5458-
MySQL支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制
5458+
MySQL 支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制
54595459

54605460
MySQL 复制的优点主要包含以下三个方面:
54615461

@@ -5486,7 +5486,7 @@ MySQL 的主从复制原理图:
54865486

54875487
- binlog dump thread:在主库事务提交时,负责把数据变更作为事件 Events 记录在二进制日志文件 binlog 中,并通知 slave 有数据更新
54885488
- I/O thread:负责从主服务器上拉取二进制日志,并将 binlog 日志内容依次写到 relay log 文件的最末端,并将新的 binlog 文件名和 offset 记录到 master-info 文件中,以便下一次读取日志时能告诉 master 服务器从指定 binlog 日志文件及位置开始读取新的 binlog 日志内容
5489-
- SQL thread:监测本地 relay log中新增了日志内容,读取中继日志并重做其中的 SQL 语句
5489+
- SQL thread:监测本地 relay log 中新增了日志内容,读取中继日志并重做其中的 SQL 语句
54905490
- 从库在 relay-log.info 中记录当前应用中继日志的文件名和位置点以便下一次执行
54915491

54925492
同步与异步:
@@ -5508,8 +5508,8 @@ MySQL 的主从复制原理图:
55085508
主从延迟就是主从之间是存在一定时间的数据不一致:
55095509

55105510
- 主库 A 执行完成一个事务,写入 binlog,该时刻记为 T1
5511-
- 传给从库B,从库接受完这个binlog的时刻记为 T2
5512-
- 从库B执行完这个事务,该时刻记为 T3
5511+
- 传给从库 B,从库接受完这个 binlog 的时刻记为 T2
5512+
- 从库 B 执行完这个事务,该时刻记为 T3
55135513

55145514
同一个事务,从库执行完成的时间和主库执行完成的时间之间的差值,即 T3-T,通过在从库执行 `show slave status` 命令,返回结果会显示 seconds_behind_master 表示当前从库延迟了多少秒
55155515

@@ -5526,13 +5526,13 @@ MySQL 的主从复制原理图:
55265526

55275527
主从同步问题永远都是**一致性和性能的权衡**,需要根据实际的应用场景,可以采取下面的办法:
55285528

5529-
* 二次查询,如果从库查不到数据,则再去主库查一遍,由 API 封装,比较简单,但导致主库压力大
5529+
* **二次查询**,如果从库查不到数据,则再去主库查一遍,由 API 封装,比较简单,但导致主库压力大
55305530
* 降低多线程大事务并发的概率,优化业务逻辑
55315531
* 优化 SQL,避免慢 SQL,减少批量操作
55325532
* 提高从库机器的配置,减少主库写 binlog 和从库读 binlog 的效率差
55335533
* 尽量采用短的链路,主库和从库服务器的距离尽量要短,提升端口带宽,减少 binlog 传输的网络延时
55345534
* 实时性要求高的业务读强制走主库,从库只做灾备,备份
5535-
* 强制将写之后立马读的操作转移到主库,比如刚注册的用户,直接登录从库查询可能查询不到,先走主库登录
5535+
* 强制将写之后**立刻读的操作转移到主库**,比如刚注册的用户,直接登录从库查询可能查询不到,先走主库登录
55365536

55375537

55385538

@@ -7620,12 +7620,12 @@ MySQL 支持 ACID 特性,保证可靠性和持久性,读取性能不高,
76207620

76217621
特征:
76227622

7623-
* 可扩容,可伸缩,SQL数据关系过于复杂,Nosql 不存关系,只存数据
7624-
* 大数据量下高性能,数据不存取在磁盘IO,存取在内存
7623+
* 可扩容,可伸缩,SQL 数据关系过于复杂,Nosql 不存关系,只存数据
7624+
* 大数据量下高性能,数据不存取在磁盘 IO,存取在内存
76257625
* 灵活的数据模型,设计了一些数据存储格式,能保证效率上的提高
76267626
* 高可用,集群
76277627

7628-
常见的Nosql:Redis、memcache、HBase、MongoDB
7628+
常见的 Nosql:Redis、memcache、HBase、MongoDB
76297629

76307630
![](https://gitee.com/seazean/images/raw/master/DB/电商场景解决方案.png)
76317631

@@ -7680,7 +7680,7 @@ Redis (REmote DIctionary Server) :用 C 语言开发的一个开源的高性
76807680

76817681
#### CentOS
76827682

7683-
1. 下载Redis
7683+
1. 下载 Redis
76847684

76857685
下载安装包:
76867686

Java.md

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13401,7 +13401,7 @@ HotSpot VM 可以通过 VM 参数设置程序执行方式:
1340113401

1340213402
- -Xint:完全采用解释器模式执行程序
1340313403
- -Xcomp:完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行
13404-
- -Xmixed:采用解释器+即时编译器的混合模式共同执行程序
13404+
- -Xmixed:采用解释器 + 即时编译器的混合模式共同执行程序
1340513405

1340613406
![](https://gitee.com/seazean/images/raw/master/Java/JVM-执行引擎工作流程.png)
1340713407

@@ -13422,11 +13422,11 @@ OSR 替换循环代码体的入口,C1、C2 替换的是方法调用的入口
1342213422

1342313423
热点探测:JIT 编译器在运行时会针热点代码做出深度优化,将其直接编译为对应平台的本地机器指令进行缓存,以提升 Java 程序的执行性能
1342413424

13425-
CodeCache 用于缓存编译后的机器码动态生成的代码和本地方法代码 JNI,如果 CodeCache 区域被占满,编译器被停用,字节码将不会编译为机器码,应用程序继续运行,但运行速度会降低一个数量级,严重影响系统性能
13425+
CodeCache 用于缓存编译后的机器码动态生成的代码和本地方法代码 JNI,如果 CodeCache 区域被占满,编译器被停用,字节码将不会编译为机器码,应用程序继续运行,但运行速度会降低一个数量级,严重影响系统性能
1342613426

13427-
HotSpot VM 采用的热点探测方式是基于计数器的热点探测,为每一个方法都建立2个不同类型的计数器:方法调用计数器(Invocation Counter)和回边计数器(BackEdge Counter)
13427+
HotSpot VM 采用的热点探测方式是基于计数器的热点探测,为每一个方法都建立 2 个不同类型的计数器:方法调用计数器(Invocation Counter)和回边计数器(BackEdge Counter)
1342813428

13429-
* 方法调用计数器:用于统计方法被调用的次数,默认阈值在Client 模式 下是1500 次,在 Server 模式下是10000 次,超过这个阈值,就会触发 JIT 编译,阈值可以通过虚拟机参数 `-XX:CompileThreshold` 设置
13429+
* 方法调用计数器:用于统计方法被调用的次数,默认阈值在 Client 模式 下是 1500 次,在 Server 模式下是10000 次,超过这个阈值,就会触发 JIT 编译,阈值可以通过虚拟机参数 `-XX:CompileThreshold` 设置
1343013430

1343113431
工作流程:当一个方法被调用时, 会先检查该方法是否存在被 JIT 编译过的版本,存在则使用编译后的本地代码来执行;如果不存在则将此方法的调用计数器值加 1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值,如果超过阈值会向即时编译器提交一个该方法的代码编译请求
1343213432

@@ -13477,7 +13477,7 @@ VM 参数设置:
1347713477

1347813478
- -client:指定 Java 虚拟机运行在 Client 模式下,并使用 C1 编译器
1347913479
- -server:指定 Java 虚拟机运行在 Server 模式下,并使用 C2 编译器
13480-
- `-server -XX:+TieredCompilation`:在1.8之前,分层编译默认是关闭的,可以添加该参数开启
13480+
- `-server -XX:+TieredCompilation`:在 1.8 之前,分层编译默认是关闭的,可以添加该参数开启
1348113481

1348213482
分层编译策略 (Tiered Compilation):程序解释执行可以触发 C1 编译,将字节码编译成机器码,加上性能监控,C2 编译会根据性能监控信息进行激进优化,JVM 将执行状态分成了 5 个层次:
1348313483

@@ -13554,7 +13554,7 @@ public static int invoke(Object... args) {
1355413554

1355513555
- 静态语言是判断变量自身的类型信息;动态类型语言是判断变量值的类型信息,变量没有类型信息
1355613556

13557-
- **Java是静态类型语言**(尽管lambda表达式为其增加了动态特性),js,python是动态类型语言
13557+
- **Java 是静态类型语言**(尽管 lambda 表达式为其增加了动态特性),js,python是动态类型语言
1355813558

1355913559
```java
1356013560
String s = "abc"; //Java
@@ -14735,15 +14735,6 @@ jstatd 是一个 RMI 服务端程序,相当于代理服务器,建立本地
1473514735

1473614736
添加 JVM 参数选项:进入 Run/Debug Configurations → VM options 设置参数
1473714737

14738-
程序运行中:
14739-
14740-
```sh
14741-
# 设置Boolean类型参数
14742-
jinfo -flag [+|-]<name> <pid>
14743-
# 设置非Boolean类型参数
14744-
jinfo -flag <name>=<value> <pid>
14745-
```
14746-
1474714738
* 标准参数选项:`java [-options] class [args...]` 或 `java [-options] -jar jarfile [args...]`
1474814739

1474914740
命令:`-? -help` 可以输出此命令的相关选项
@@ -14800,7 +14791,17 @@ jinfo -flag <name>=<value> <pid>
1480014791
-XX:<option>=<string> 设置option字符值
1480114792
```
1480214793

14803-
14794+
14795+
程序运行中:
14796+
14797+
```sh
14798+
# 设置Boolean类型参数
14799+
jinfo -flag [+|-]<name> <pid>
14800+
# 设置非Boolean类型参数
14801+
jinfo -flag <name>=<value> <pid>
14802+
```
14803+
14804+
1480414805

1480514806
****
1480614807

@@ -14861,10 +14862,10 @@ jinfo -flag <name>=<value> <pid>
1486114862
#### OOM参数
1486214863

1486314864
```sh
14864-
-XX:+HeapDumpOnOutMemoryError 内存出现OOM时生成Heap转储文件,两者互斥
14865-
-XX:+HeapDumpBeforeFullGC 出现FullGC时生成Heap转储文件,两者互斥
14866-
-XX:HeapDumpPath=<path> 指定heap转储文件的存储路径,默认当前目录
14867-
-XX:OnOutOfMemoryError=<path> 指定可行性程序或脚本的路径,当发生OOM时执行脚本
14865+
-XX:+HeapDumpOnOutMemoryError 内存出现OOM时生成Heap转储文件,两者互斥
14866+
-XX:+HeapDumpBeforeFullGC 出现FullGC时生成Heap转储文件,两者互斥
14867+
-XX:HeapDumpPath=<path> 指定heap转储文件的存储路径,默认当前目录
14868+
-XX:OnOutOfMemoryError=<path> 指定可行性程序或脚本的路径,当发生OOM时执行脚本
1486814869
```
1486914870

1487014871

@@ -15016,7 +15017,7 @@ Full GC 日志:
1501615017

1501715018
- 括号外:GC 回收前年轻代和老年代大小 -> 回收后大小(年轻代和老年代总大小)
1501815019

15019-
* Minor GC 堆内存总容量 = 9/10 年轻代 + 老年代,Survivor区只计算 from 部分,而 JVM 默认年轻代中 Eden 区和 Survivor 区的比例关系:Eden:S0:S1=8:1:1
15020+
* Minor GC 堆内存总容量 = 9/10 年轻代 + 老年代,Survivor 区只计算 from 部分,而 JVM 默认年轻代中 Eden 区和 Survivor 区的比例关系:Eden:S0:S1=8:1:1
1502015021

1502115022
通过日志看 GC 时间:GC 日志中有三个时间 user、sys、real
1502215023

@@ -15605,18 +15606,19 @@ public class MergeSort {
1560515606
}
1560615607

1560715608
private static void merge(int[] arr, int low, int mid, int high) {
15608-
int m = 0;
15609+
int index = 0;
1560915610
//定义左右指针
1561015611
int left = low, right = mid + 1;
1561115612
int[] assist = new int[high - low + 1];
15613+
1561215614
while (left <= mid && right <= high) {
15613-
assist[m++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
15615+
assist[index++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
1561415616
}
1561515617
while (left <= mid) {
15616-
assist[m++] = arr[left++];
15618+
assist[index++] = arr[left++];
1561715619
}
1561815620
while (right <= high) {
15619-
assist[m++] = arr[right++];
15621+
assist[index++] = arr[right++];
1562015622
}
1562115623

1562215624
for (int k = 0; k < assist.length; k++) {
@@ -16479,6 +16481,7 @@ public void union(int p, int q) {
1647916481
}
1648016482
//让p所在树的节点根节点为q的所在的根节点,只需要把根节点改一下,时间复杂度 O(1)
1648116483
eleAndGroup[pRoot] = qRoot;
16484+
this.count-
1648216485
}
1648316486
```
1648416487

@@ -16793,7 +16796,7 @@ public class MGraph {
1679316796

1679416797
### 工作流程
1679516798

16796-
向布隆过滤器中添加一个元素key时,会通过多个hash函数得到多个哈希值,在位数组中把对应下标的值置为 1
16799+
向布隆过滤器中添加一个元素 key 时,会通过多个 hash 函数得到多个哈希值,在位数组中把对应下标的值置为 1
1679716800

1679816801
![](https://gitee.com/seazean/images/raw/master/DB/Redis-布隆过滤器添加数据.png)
1679916802

Prog.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3671,7 +3671,7 @@ ThreadLocal 作用:
36713671

36723672
| 方法 | 描述 |
36733673
| -------------------------- | ---------------------------- |
3674-
| ThreadLocal<>() | 创建ThreadLocal对象 |
3674+
| ThreadLocal<>() | 创建 ThreadLocal 对象 |
36753675
| protected T initialValue() | 返回当前线程局部变量的初始值 |
36763676
| public void set( T value) | 设置当前线程绑定的局部变量 |
36773677
| public T get() | 获取当前线程绑定的局部变量 |
@@ -3783,23 +3783,23 @@ public class ThreadLocalDateUtil {
37833783

37843784
#### 底层结构
37853785

3786-
JDK8以前:每个ThreadLocal都创建一个Map,然后用线程作为Map的key,要存储的局部变量作为Map的value,这样就能达到各个线程的局部变量隔离的效果
3786+
JDK8 以前:每个 ThreadLocal 都创建一个 Map,然后用线程作为 Map 的 key,要存储的局部变量作为 Map 的 value,这样就能达到各个线程的局部变量隔离的效果
37873787

37883788
![](https://gitee.com/seazean/images/raw/master/Java/JUC-ThreadLocal数据结构JDK8前.png)
37893789

3790-
JDK8以后:每个Thread维护一个ThreadLocalMap,这个Map的key是ThreadLocal实例本身,value才是真正要存储的值Object
3790+
JDK8 以后:每个 Thread 维护一个 ThreadLocalMap,这个 Map 的 key 是 ThreadLocal 实例本身,value 才是真正要存储的值 Object
37913791

3792-
* 每个Thread线程内部都有一个Map (ThreadLocalMap)
3793-
* Map里面存储ThreadLocal对象(key)和线程的变量副本(value)
3794-
* Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值
3792+
* 每个 Thread 线程内部都有一个 Map (ThreadLocalMap)
3793+
* Map 里面存储 ThreadLocal 对象(key)和线程的变量副本(value)
3794+
* Thread 内部的 Map 是由 ThreadLocal 维护的,由 ThreadLocal 负责向 map 获取和设置线程的变量值
37953795
* 对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成副本的隔离,互不干扰
37963796

37973797
![](https://gitee.com/seazean/images/raw/master/Java/JUC-ThreadLocal数据结构JDK8后.png)
37983798

3799-
JDK8前后对比
3799+
JDK8 前后对比
38003800

3801-
* 每个Map存储的Entry数量会变少,因为之前的存储数量由Thread的数量决定,现在由ThreadLocal的数量决定,在实际编程当中,往往ThreadLocal的数量要少于Thread的数量
3802-
* Thread销毁之后,对应的ThreadLocalMap也会随之销毁,能减少内存的使用
3801+
* 每个 Map 存储的 Entry 数量会变少,因为之前的存储数量由 Thread 的数量决定,现在由 ThreadLocal 的数量决定,在实际编程当中,往往 ThreadLocal 的数量要少于 Thread 的数量
3802+
* Thread 销毁之后,对应的 ThreadLocalMap 也会随之销毁,能减少内存的使用
38033803

38043804

38053805

@@ -9676,11 +9676,11 @@ epoll 的特点:
96769676

96779677
用户空间:用户代码、用户堆栈
96789678

9679-
内核空间:内核代码、内核调度程序、进程描述符(内核堆栈、thread_info进程描述符
9679+
内核空间:内核代码、内核调度程序、进程描述符(内核堆栈、thread_info 进程描述符
96809680

96819681
* 进程描述符和用户的进程是一一对应的
96829682
* SYS_API 系统调用:如 read、write,系统调用就是 0X80 中断
9683-
* 进程描述符pd:进程从用户态切换到内核态时,需要保存用户态时的上下文信息,
9683+
* 进程描述符 pd:进程从用户态切换到内核态时,需要保存用户态时的上下文信息,
96849684
* 线程上下文:用户程序基地址,程序计数器、cpu cache、寄存器等,方便程序切回用户态时恢复现场
96859685
* 内核堆栈:系统调用函数也是要创建变量的,这些变量在内核堆栈上分配
96869686

@@ -9701,7 +9701,7 @@ epoll 的特点:
97019701
- 发起 `0X80` 中断
97029702
- 程序执行碰到除 0 异常
97039703

9704-
系统调用 system_call 函数所对应的中断指令编号是 0X80十进制是8×16=128),而该指令编号对应的就是系统调用程序的入口,所以称系统调用为 80 中断
9704+
系统调用 system_call 函数所对应的中断指令编号是 0X80十进制是 8×16=128),而该指令编号对应的就是系统调用程序的入口,所以称系统调用为 80 中断
97059705

97069706
系统调用的流程:
97079707

@@ -9714,8 +9714,6 @@ epoll 的特点:
97149714

97159715

97169716

9717-
参考文章:https://blog.csdn.net/hancoder/article/details/112149121
9718-
97199717

97209718

97219719
****
@@ -9778,7 +9776,7 @@ read 调用图示:read、write 都是系统调用指令
97789776

97799777
#### mmap
97809778

9781-
mmap(Memory Mapped Files)加 write 实现零拷贝,零拷贝就是没有数据从内核空间复制到用户空间
9779+
mmap(Memory Mapped Files)加 write 实现零拷贝,**零拷贝就是没有数据从内核空间复制到用户空间**
97829780

97839781
用户空间和内核空间都使用内存,所以可以共享同一块物理内存地址,省去用户态和内核态之间的拷贝。写网卡时,共享空间的内容拷贝到 socket 缓冲区,然后交给 DMA 发送到网卡,只需要 3 次复制
97849782

@@ -9811,7 +9809,7 @@ sendfile 实现零拷贝,打开文件的文件描述符 fd 和 socket 的 fd
98119809

98129810
sendfile2.4 之后,sendfile 实现了更简单的方式,文件到达内核缓冲区后,不必再将数据全部复制到 socket buffer 缓冲区,而是只**将记录数据位置和长度相关等描述符信息**保存到 socket buffer,DMA 根据 socket 缓冲区中描述符提供的位置和偏移量信息直接将内核空间缓冲区中的数据拷贝到协议引擎上(2 次复制 2 次切换)
98139811

9814-
Java NIO 对 sendfile 的支持是 `FileChannel.transferTo()/transferFrom()`,把磁盘文件读取 OS 内核缓冲区后的 fileChannel,直接转给 socketChannel 发送,底层就是sendfile
9812+
Java NIO 对 sendfile 的支持是 `FileChannel.transferTo()/transferFrom()`,把磁盘文件读取 OS 内核缓冲区后的 fileChannel,直接转给 socketChannel 发送,底层就是 sendfile
98159813

98169814

98179815

0 commit comments

Comments
 (0)