mirror of
https://github.com/youthlql/JavaYouth.git
synced 2026-06-15 20:07:04 +00:00
图床更新,请注意查看readme
This commit is contained in:
29
README.md
29
README.md
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
> 1. 文章会优先发布在[Github](https://github.com/youthlql/JavaYouth),其它平台会晚一段时间,文章纠错与更新内容只在Github。如果Github很卡,可以在去[Gitee](https://gitee.com/youthlql/JavaYouth)克隆。
|
> 1. 提供在线阅读方式:[个人自建博客](https://imlql.cn/),[CSDN博客](https://blog.csdn.net/Youth_lql)。目前图床开了防盗链,只可以在CSDN,自建博客,Github上在线看文章,下载到本地是无法看到图片的。
|
||||||
> 2. 提供在线阅读方式:[个人博客](https://imlql.cn/)。原先的电子书已关闭
|
|
||||||
> 3. 转载须知:转载请注明GitHub出处,让我们一起维护一个良好的技术创作环境。
|
> 3. 转载须知:转载请注明GitHub出处,让我们一起维护一个良好的技术创作环境。
|
||||||
> 4. 如果你要提交 issue 或者 pr 的话建议到 [Github](https://github.com/youthlql/JavaYouth) 提交。笔者会持续更新,如果对你有所帮助,不妨[Github](https://github.com/youthlql/JavaYouth)点个**Star~**,你的**Star**是我创作的动力。
|
> 4. 如果你要提交 issue 或者 pr 的话建议到 [Github](https://github.com/youthlql/JavaYouth) 提交。笔者会持续更新,如果对你有所帮助,不妨[Github](https://github.com/youthlql/JavaYouth)点个**Star~**,你的**Star**是我创作的动力。
|
||||||
> 5. 提供所有文章的本地版本【Typora可直接打开】
|
> 5. 提供所有文章的本地版本【Typora可直接打开,可以看到图片】
|
||||||
> 1. 阿里云盘:https://www.aliyundrive.com/s/ZNAPANQg54A
|
> 1. 阿里云盘:https://www.aliyundrive.com/s/ZNAPANQg54A
|
||||||
> 2. 百度云盘:https://pan.baidu.com/s/1V20nKp4TQai-SVwMAfSZtQ 提取码:4ozs
|
> 2. 百度云盘:https://pan.baidu.com/s/1V20nKp4TQai-SVwMAfSZtQ 提取码:4ozs
|
||||||
|
|
||||||
@@ -88,16 +87,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 计算机网络
|
|
||||||
|
|
||||||
**总结篇**
|
|
||||||
|
|
||||||
[计算机网络-总结-秋招篇](docs/Computer_NetWork/计算机网络-总结.md)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[计算机网络-概述](docs/Computer_NetWork/计算机网络-概述.md)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 操作系统
|
# 操作系统
|
||||||
@@ -189,17 +178,3 @@
|
|||||||
6. [Dubbo服务调用源码解析](docs/dubbo-sourcecode-v1/07.Dubbo源码系列V1-Dubbo第七节-服务调用源码解析.md)
|
6. [Dubbo服务调用源码解析](docs/dubbo-sourcecode-v1/07.Dubbo源码系列V1-Dubbo第七节-服务调用源码解析.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Apollo
|
|
||||||
|
|
||||||
[Apollo简单入门](docs/Apollo/Apollo简单入门.md)
|
|
||||||
|
|
||||||
# ElasticSearch
|
|
||||||
|
|
||||||
## 用法
|
|
||||||
|
|
||||||
1、[ElasticSearch-入门](docs/ElasticSearch/usage/ElasticSearch-入门.md)
|
|
||||||
|
|
||||||
2、[ElasticSearch-进阶](docs/ElasticSearch/usage/ElasticSearch-进阶.md)
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第10章-垃圾回收概述和相关算法。
|
description: JVM系列-第10章-垃圾回收概述和相关算法。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: d54daa0f
|
abbrlink: d54daa0f
|
||||||
date: 2020-11-16 18:14:02
|
date: 2020-11-16 18:14:02
|
||||||
---
|
---
|
||||||
@@ -20,7 +20,7 @@ date: 2020-11-16 18:14:02
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0001.png">
|
||||||
|
|
||||||
1. Java 和 C++语言的区别,就在于垃圾收集技术和内存动态分配上,C++语言没有垃圾收集技术,需要程序员手动的收集。
|
1. Java 和 C++语言的区别,就在于垃圾收集技术和内存动态分配上,C++语言没有垃圾收集技术,需要程序员手动的收集。
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ date: 2020-11-16 18:14:02
|
|||||||
|
|
||||||
**十几年前磁盘碎片整理的日子**
|
**十几年前磁盘碎片整理的日子**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0002.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0002.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ date: 2020-11-16 18:14:02
|
|||||||
|
|
||||||
### 应该关心哪些区域的回收?
|
### 应该关心哪些区域的回收?
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0003.png">
|
||||||
|
|
||||||
1. 垃圾收集器可以对年轻代回收,也可以对老年代回收,甚至是全栈和方法区的回收,
|
1. 垃圾收集器可以对年轻代回收,也可以对老年代回收,甚至是全栈和方法区的回收,
|
||||||
1. 其中,**Java堆是垃圾收集器的工作重点**
|
1. 其中,**Java堆是垃圾收集器的工作重点**
|
||||||
@@ -216,7 +216,7 @@ date: 2020-11-16 18:14:02
|
|||||||
|
|
||||||
### 循环引用
|
### 循环引用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0004.png">
|
||||||
|
|
||||||
当p的指针断开的时候,内部的引用形成一个循环,计数器都还算1,无法被回收,这就是循环引用,从而造成内存泄漏
|
当p的指针断开的时候,内部的引用形成一个循环,计数器都还算1,无法被回收,这就是循环引用,从而造成内存泄漏
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ public class RefCountGC {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0005.png">
|
||||||
|
|
||||||
* 如果不小心直接把`obj1.reference`和`obj2.reference`置为null。则在Java堆中的两块内存依然保持着互相引用,无法被回收
|
* 如果不小心直接把`obj1.reference`和`obj2.reference`置为null。则在Java堆中的两块内存依然保持着互相引用,无法被回收
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ Process finished with exit code 0
|
|||||||
3. 如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。
|
3. 如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。
|
||||||
4. 在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。
|
4. 在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -371,7 +371,7 @@ Process finished with exit code 0
|
|||||||
- 基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutofMemoryError),系统类加载器。
|
- 基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutofMemoryError),系统类加载器。
|
||||||
7. 反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
|
7. 反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -453,7 +453,7 @@ Object 类中 finalize() 源码
|
|||||||
|
|
||||||
**通过 JVisual VM 查看 Finalizer 线程**
|
**通过 JVisual VM 查看 Finalizer 线程**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0008.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -579,7 +579,7 @@ MAT与JProfiler的GC Roots溯源
|
|||||||
|
|
||||||
**方式一:命令行使用 jmap**
|
**方式一:命令行使用 jmap**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -631,23 +631,23 @@ public class GCRootsTest {
|
|||||||
|
|
||||||
1、先执行第一步,然后停下来,去生成此步骤dump文件
|
1、先执行第一步,然后停下来,去生成此步骤dump文件
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0010.png">
|
||||||
|
|
||||||
2、 点击【堆 Dump】
|
2、 点击【堆 Dump】
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0011.png">
|
||||||
|
|
||||||
3、右键 --\> 另存为即可
|
3、右键 --\> 另存为即可
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0012.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0012.jpg">
|
||||||
|
|
||||||
4、输入命令,继续执行程序
|
4、输入命令,继续执行程序
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0013.png">
|
||||||
|
|
||||||
5、我们接着捕获第二张堆内存快照
|
5、我们接着捕获第二张堆内存快照
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0014.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0014.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -657,19 +657,19 @@ public class GCRootsTest {
|
|||||||
|
|
||||||
> 点击Open Heap Dump也行
|
> 点击Open Heap Dump也行
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0015.png">
|
||||||
|
|
||||||
2、选择Java Basics --> GC Roots
|
2、选择Java Basics --> GC Roots
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0016.png">
|
||||||
|
|
||||||
3、第一次捕捉堆内存快照时,GC Roots 中包含我们定义的两个局部变量,类型分别为 ArrayList 和 Date,Total:21
|
3、第一次捕捉堆内存快照时,GC Roots 中包含我们定义的两个局部变量,类型分别为 ArrayList 和 Date,Total:21
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0017.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0017.jpg">
|
||||||
|
|
||||||
4、打开第二个dump文件,第二次捕获内存快照时,由于两个局部变量引用的对象被释放,所以这两个局部变量不再作为 GC Roots ,从 Total Entries = 19 也可以看出(少了两个 GC Roots)
|
4、打开第二个dump文件,第二次捕获内存快照时,由于两个局部变量引用的对象被释放,所以这两个局部变量不再作为 GC Roots ,从 Total Entries = 19 也可以看出(少了两个 GC Roots)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0018.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0018.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -716,13 +716,13 @@ public class GCRootsTest {
|
|||||||
|
|
||||||
1、
|
1、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0019.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0019.jpg">
|
||||||
|
|
||||||
2、
|
2、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0020.png">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0021.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0021.jpg">
|
||||||
|
|
||||||
可以发现颜色变绿了,可以动态的看变化
|
可以发现颜色变绿了,可以动态的看变化
|
||||||
|
|
||||||
@@ -730,11 +730,11 @@ public class GCRootsTest {
|
|||||||
|
|
||||||
3、右击对象,选择 Show Selection In Heap Walker,单独的查看某个对象
|
3、右击对象,选择 Show Selection In Heap Walker,单独的查看某个对象
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0022.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0023.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -742,13 +742,13 @@ public class GCRootsTest {
|
|||||||
|
|
||||||
点击Show Paths To GC Roots,在弹出界面中选择默认设置即可
|
点击Show Paths To GC Roots,在弹出界面中选择默认设置即可
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0024.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0024.jpg">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0025.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0025.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0026.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0026.png">
|
||||||
|
|
||||||
### JProfiler 分析 OOM
|
### JProfiler 分析 OOM
|
||||||
|
|
||||||
@@ -798,11 +798,11 @@ count = 6
|
|||||||
|
|
||||||
1、看这个超大对象
|
1、看这个超大对象
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0027.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0027.png">
|
||||||
|
|
||||||
2、揪出 main() 线程中出问题的代码
|
2、揪出 main() 线程中出问题的代码
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0028.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0028.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -837,7 +837,7 @@ count = 6
|
|||||||
* 注意:标记的是被引用的对象,也就是可达对象,并非标记的是即将被清除的垃圾对象
|
* 注意:标记的是被引用的对象,也就是可达对象,并非标记的是即将被清除的垃圾对象
|
||||||
2. 清除:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收
|
2. 清除:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0029.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0029.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -876,7 +876,7 @@ count = 6
|
|||||||
|
|
||||||
将活着的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收
|
将活着的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0030.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0030.png">
|
||||||
|
|
||||||
新生代里面就用到了复制算法,Eden区和S0区存活对象整体复制到S1区
|
新生代里面就用到了复制算法,Eden区和S0区存活对象整体复制到S1区
|
||||||
|
|
||||||
@@ -906,7 +906,7 @@ count = 6
|
|||||||
2. 老年代大量的对象存活,那么复制的对象将会有很多,效率会很低
|
2. 老年代大量的对象存活,那么复制的对象将会有很多,效率会很低
|
||||||
3. 在新生代,对常规应用的垃圾回收,一次通常可以回收70% - 99% 的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
|
3. 在新生代,对常规应用的垃圾回收,一次通常可以回收70% - 99% 的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0031.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0031.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -935,7 +935,7 @@ count = 6
|
|||||||
2. 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间。
|
2. 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0032.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0032.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1066,7 +1066,7 @@ A:无,没有最好的算法,只有最合适的算法
|
|||||||
1. 一般来说,在相同条件下,堆空间越大,一次GC时所需要的时间就越长,有关GC产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。
|
1. 一般来说,在相同条件下,堆空间越大,一次GC时所需要的时间就越长,有关GC产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。
|
||||||
3. 分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间。每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。
|
3. 分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间。每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0033.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0033.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第11章-垃圾回收相关概念。
|
description: JVM系列-第11章-垃圾回收相关概念。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 4d401a8b
|
abbrlink: 4d401a8b
|
||||||
date: 2020-11-17 12:33:24
|
date: 2020-11-17 12:33:24
|
||||||
---
|
---
|
||||||
@@ -117,7 +117,7 @@ JVM参数:
|
|||||||
|
|
||||||
2、我也查过了大对象阈值的默认值
|
2、我也查过了大对象阈值的默认值
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0001.png">
|
||||||
|
|
||||||
我不太懂这个默认值为啥是0,我猜测可能是代表什么比例,目前也没有搜到相关的东西。这个不太重要,暂时就没有太深究,希望读者有知道的可以告知我一声。
|
我不太懂这个默认值为啥是0,我猜测可能是代表什么比例,目前也没有搜到相关的东西。这个不太重要,暂时就没有太深究,希望读者有知道的可以告知我一声。
|
||||||
|
|
||||||
@@ -185,11 +185,11 @@ Heap
|
|||||||
|
|
||||||
1、来看看字节码:实例方法局部变量表第一个变量肯定是 this
|
1、来看看字节码:实例方法局部变量表第一个变量肯定是 this
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0002.png">
|
||||||
|
|
||||||
2、你有没有看到,局部变量表的大小是 2。但是局部变量表里只有一个索引为0的啊?那索引为1的是哪个局部变量呢?实际上索引为1的位置是buffer在占用着,执行 System.gc() 时,栈中还有 buffer 变量指向堆中的字节数组,所以没有进行GC
|
2、你有没有看到,局部变量表的大小是 2。但是局部变量表里只有一个索引为0的啊?那索引为1的是哪个局部变量呢?实际上索引为1的位置是buffer在占用着,执行 System.gc() 时,栈中还有 buffer 变量指向堆中的字节数组,所以没有进行GC
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0003.png">
|
||||||
|
|
||||||
3、那么这种代码块的情况,什么时候会被GC呢?我们来看第四个方法
|
3、那么这种代码块的情况,什么时候会被GC呢?我们来看第四个方法
|
||||||
|
|
||||||
@@ -217,11 +217,11 @@ A:局部变量表长度为 2 ,这说明了出了代码块时,buffer 就出
|
|||||||
|
|
||||||
> 这点看不懂的可以看我前面的文章:虚拟机栈 --> Slot的重复利用
|
> 这点看不懂的可以看我前面的文章:虚拟机栈 --> Slot的重复利用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ Heap
|
|||||||
|
|
||||||
右边的图:后期有一些对象不用了,按道理应该断开引用,但是存在一些链没有断开(图示中的Forgotten Reference Memory Leak),从而导致没有办法被回收。
|
右边的图:后期有一些对象不用了,按道理应该断开引用,但是存在一些链没有断开(图示中的Forgotten Reference Memory Leak),从而导致没有办法被回收。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -446,7 +446,7 @@ Process finished with exit code -1
|
|||||||
2. 并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几个时间区间之间来回切换。由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应用程序同时在进行
|
2. 并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几个时间区间之间来回切换。由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应用程序同时在进行
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ Process finished with exit code -1
|
|||||||
3. 适合科学计算,后台处理等弱交互场景
|
3. 适合科学计算,后台处理等弱交互场景
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0008.png">
|
||||||
|
|
||||||
> **并发与并行的对比**
|
> **并发与并行的对比**
|
||||||
|
|
||||||
@@ -482,7 +482,7 @@ Process finished with exit code -1
|
|||||||
* 相较于并行的概念,单线程执行。
|
* 相较于并行的概念,单线程执行。
|
||||||
* 如果内存不够,则程序暂停,启动JVM垃圾回收器进行垃圾回收(单线程)
|
* 如果内存不够,则程序暂停,启动JVM垃圾回收器进行垃圾回收(单线程)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -492,7 +492,7 @@ Process finished with exit code -1
|
|||||||
- 比如用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;
|
- 比如用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;
|
||||||
2. 典型垃圾回收器:CMS、G1
|
2. 典型垃圾回收器:CMS、G1
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -551,7 +551,7 @@ Process finished with exit code -1
|
|||||||
|
|
||||||
1、一般的垃圾回收算法至少会划分出两个年代,年轻代和老年代。但是单纯的分代理论在垃圾回收的时候存在一个巨大的缺陷:为了找到年轻代中的存活对象,却不得不遍历整个老年代,反过来也是一样的。
|
1、一般的垃圾回收算法至少会划分出两个年代,年轻代和老年代。但是单纯的分代理论在垃圾回收的时候存在一个巨大的缺陷:为了找到年轻代中的存活对象,却不得不遍历整个老年代,反过来也是一样的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0011.png">
|
||||||
|
|
||||||
2、如果我们从年轻代开始遍历,那么可以断定N, S, P, Q都是存活对象。但是,V却不会被认为是存活对象,其占据的内存会被回收了。这就是一个惊天的大漏洞!因为U本身是老年代对象,而且有外部引用指向它,也就是说U是存活对象,而U指向了V,也就是说V也应该是存活对象才是!而这都是因为我们只遍历年轻代对象!
|
2、如果我们从年轻代开始遍历,那么可以断定N, S, P, Q都是存活对象。但是,V却不会被认为是存活对象,其占据的内存会被回收了。这就是一个惊天的大漏洞!因为U本身是老年代对象,而且有外部引用指向它,也就是说U是存活对象,而U指向了V,也就是说V也应该是存活对象才是!而这都是因为我们只遍历年轻代对象!
|
||||||
|
|
||||||
@@ -599,7 +599,7 @@ Process finished with exit code -1
|
|||||||
4. 这4种引用强度依次逐渐减弱。除强引用外,其他3种引用均可以在java.lang.ref包中找到它们的身影。如下图,显示了这3种引用类型对应的类,开发人员可以在应用程序中直接使用它们。
|
4. 这4种引用强度依次逐渐减弱。除强引用外,其他3种引用均可以在java.lang.ref包中找到它们的身影。如下图,显示了这3种引用类型对应的类,开发人员可以在应用程序中直接使用它们。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0012.png" >
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0012.png" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -661,7 +661,7 @@ Hello,尚硅谷
|
|||||||
|
|
||||||
`StringBuffer str = new StringBuffer("hello,尚硅谷");`
|
`StringBuffer str = new StringBuffer("hello,尚硅谷");`
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第12章-垃圾回收器。
|
description: JVM系列-第12章-垃圾回收器。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 7706d61d
|
abbrlink: 7706d61d
|
||||||
date: 2020-11-19 18:33:24
|
date: 2020-11-19 18:33:24
|
||||||
---
|
---
|
||||||
@@ -44,7 +44,7 @@ GC 分类与性能指标
|
|||||||
|
|
||||||
**按线程数分(垃圾回收线程数),可以分为串行垃圾回收器和并行垃圾回收器。**
|
**按线程数分(垃圾回收线程数),可以分为串行垃圾回收器和并行垃圾回收器。**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0001.png">
|
||||||
|
|
||||||
1. 串行回收指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束。
|
1. 串行回收指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束。
|
||||||
1. 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器。所以,串行回收默认被应用在客户端的Client模式下的JVM中
|
1. 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器。所以,串行回收默认被应用在客户端的Client模式下的JVM中
|
||||||
@@ -60,7 +60,7 @@ GC 分类与性能指标
|
|||||||
1. 并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。
|
1. 并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。
|
||||||
2. 独占式垃圾回收器(Stop the World)一旦运行,就停止应用程序中的所有用户线程,直到垃圾回收过程完全结束。
|
2. 独占式垃圾回收器(Stop the World)一旦运行,就停止应用程序中的所有用户线程,直到垃圾回收过程完全结束。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ GC 分类与性能指标
|
|||||||
2. 这种情况下,应用程序能容忍较高的暂停时间,因此,高吞吐量的应用程序有更长的时间基准,快速响应是不必考虑的
|
2. 这种情况下,应用程序能容忍较高的暂停时间,因此,高吞吐量的应用程序有更长的时间基准,快速响应是不必考虑的
|
||||||
3. 吞吐量优先,意味着在单位时间内,STW的时间最短:0.2+0.2=0.4
|
3. 吞吐量优先,意味着在单位时间内,STW的时间最短:0.2+0.2=0.4
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ GC 分类与性能指标
|
|||||||
- 例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的
|
- 例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的
|
||||||
2. 暂停时间优先,意味着尽可能让单次STW的时间最短:0.1+0.1 + 0.1+ 0.1+ 0.1=0.5,但是总的GC时间可能会长
|
2. 暂停时间优先,意味着尽可能让单次STW的时间最短:0.1+0.1 + 0.1+ 0.1+ 0.1=0.5,但是总的GC时间可能会长
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -169,17 +169,17 @@ GC 分类与性能指标
|
|||||||
2. 并行回收器:ParNew、Parallel Scavenge、Parallel old
|
2. 并行回收器:ParNew、Parallel Scavenge、Parallel old
|
||||||
3. 并发回收器:CMS、G1
|
3. 并发回收器:CMS、G1
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0005.png">
|
||||||
|
|
||||||
**官方文档**
|
**官方文档**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0006.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0006.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**7款经典回收器与垃圾分代之间的关系**
|
**7款经典回收器与垃圾分代之间的关系**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0007.png">
|
||||||
|
|
||||||
1. 新生代收集器:Serial、ParNew、Parallel Scavenge;
|
1. 新生代收集器:Serial、ParNew、Parallel Scavenge;
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ GC 分类与性能指标
|
|||||||
|
|
||||||
### 垃圾收集器的组合关系
|
### 垃圾收集器的组合关系
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0008.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -251,11 +251,11 @@ jinfo -flag UseParallelOldGC 进程id
|
|||||||
|
|
||||||
JDK 8 中默认使用 ParallelGC 和 ParallelOldGC 的组合
|
JDK 8 中默认使用 ParallelGC 和 ParallelOldGC 的组合
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0009.png">
|
||||||
|
|
||||||
#### JDK9
|
#### JDK9
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ Serial 回收器:串行回收
|
|||||||
|
|
||||||
这个收集器是一个单线程的收集器,“单线程”的意义:它只会使用一个CPU(串行)或一条收集线程去完成垃圾收集工作。更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World)
|
这个收集器是一个单线程的收集器,“单线程”的意义:它只会使用一个CPU(串行)或一条收集线程去完成垃圾收集工作。更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0011.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ ParNew 回收器:并行回收
|
|||||||
2. ParNew 收集器除了采用**并行回收**的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。
|
2. ParNew 收集器除了采用**并行回收**的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。
|
||||||
3. ParNew 是很多JVM运行在Server模式下新生代的默认垃圾收集器。
|
3. ParNew 是很多JVM运行在Server模式下新生代的默认垃圾收集器。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0012.png">
|
||||||
|
|
||||||
1. 对于新生代,回收次数频繁,使用并行方式高效。
|
1. 对于新生代,回收次数频繁,使用并行方式高效。
|
||||||
2. 对于老年代,回收次数少,使用串行方式节省资源。(CPU并行需要切换线程,串行可以省去切换线程的资源)
|
2. 对于老年代,回收次数少,使用串行方式节省资源。(CPU并行需要切换线程,串行可以省去切换线程的资源)
|
||||||
@@ -361,7 +361,7 @@ Parallel 回收器:吞吐量优先
|
|||||||
5. Parallel Old收集器采用了标记-压缩算法,但同样也是基于并行回收和"Stop-the-World"机制。
|
5. Parallel Old收集器采用了标记-压缩算法,但同样也是基于并行回收和"Stop-the-World"机制。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0013.png">
|
||||||
|
|
||||||
1. 在程序吞吐量优先的应用场景中,Parallel收集器和Parallel Old收集器的组合,在server模式下的内存回收性能很不错。
|
1. 在程序吞吐量优先的应用场景中,Parallel收集器和Parallel Old收集器的组合,在server模式下的内存回收性能很不错。
|
||||||
2. **在Java8中,默认是此垃圾收集器。**
|
2. **在Java8中,默认是此垃圾收集器。**
|
||||||
@@ -418,7 +418,7 @@ CMS 回收器:低延迟
|
|||||||
|
|
||||||
### CMS 工作原理(过程)
|
### CMS 工作原理(过程)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0014.png">
|
||||||
|
|
||||||
CMS整个过程比之前的收集器要复杂,整个过程分为4个主要阶段,即初始标记阶段、并发标记阶段、重新标记阶段和并发清除阶段。(涉及STW的阶段主要是:初始标记 和 重新标记)
|
CMS整个过程比之前的收集器要复杂,整个过程分为4个主要阶段,即初始标记阶段、并发标记阶段、重新标记阶段和并发清除阶段。(涉及STW的阶段主要是:初始标记 和 重新标记)
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ CMS整个过程比之前的收集器要复杂,整个过程分为4个主要阶
|
|||||||
3. 另外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,**而是当堆内存使用率达到某一阈值时,便开始进行回收**,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次**“Concurrent Mode Failure”** 失败,这时虚拟机将启动后备预案:临时启用Serial old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
|
3. 另外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,**而是当堆内存使用率达到某一阈值时,便开始进行回收**,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次**“Concurrent Mode Failure”** 失败,这时虚拟机将启动后备预案:临时启用Serial old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
|
||||||
4. CMS收集器的垃圾收集算法采用的是**标记清除算法**,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,**不可避免地将会产生一些内存碎片**。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer)技术,而只能够选择空闲列表(Free List)执行内存分配。
|
4. CMS收集器的垃圾收集算法采用的是**标记清除算法**,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,**不可避免地将会产生一些内存碎片**。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer)技术,而只能够选择空闲列表(Free List)执行内存分配。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -558,11 +558,11 @@ G1 回收器:区域化分代式
|
|||||||
|
|
||||||
G1的分代,已经不是下面这样的了
|
G1的分代,已经不是下面这样的了
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0016.png">
|
||||||
|
|
||||||
G1的分区是这样的一个区域
|
G1的分区是这样的一个区域
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0017.png">
|
||||||
|
|
||||||
**空间整合**
|
**空间整合**
|
||||||
|
|
||||||
@@ -655,7 +655,7 @@ G1中提供了三种垃圾回收模式:YoungGC、Mixed GC和Full GC,在不
|
|||||||
>
|
>
|
||||||
> 如图所示,可以将区域分配到Eden,幸存者和旧时代区域。 此外,还有第四种类型的物体被称为巨大区域。 这些区域旨在容纳标准区域大小的50%或更大的对象。 它们存储为一组连续区域。 最后,最后一种区域类型是堆的未使用区域。
|
> 如图所示,可以将区域分配到Eden,幸存者和旧时代区域。 此外,还有第四种类型的物体被称为巨大区域。 这些区域旨在容纳标准区域大小的50%或更大的对象。 它们存储为一组连续区域。 最后,最后一种区域类型是堆的未使用区域。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0018.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -667,7 +667,7 @@ G1中提供了三种垃圾回收模式:YoungGC、Mixed GC和Full GC,在不
|
|||||||
|
|
||||||
**Regio的细节**
|
**Regio的细节**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0019.png">
|
||||||
|
|
||||||
1. 每个Region都是通过指针碰撞来分配空间
|
1. 每个Region都是通过指针碰撞来分配空间
|
||||||
2. G1为每一个Region设 计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置以上。
|
2. G1为每一个Region设 计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置以上。
|
||||||
@@ -686,7 +686,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节:
|
|||||||
* 混合回收(Mixed GC)
|
* 混合回收(Mixed GC)
|
||||||
* (如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)
|
* (如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0020.png">
|
||||||
|
|
||||||
顺时针,Young GC --> Young GC+Concurrent Marking --> Mixed GC顺序,进行垃圾回收
|
顺时针,Young GC --> Young GC+Concurrent Marking --> Mixed GC顺序,进行垃圾回收
|
||||||
|
|
||||||
@@ -731,7 +731,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0021.png">
|
||||||
|
|
||||||
1. 在回收 Region 时,为了不进行全堆的扫描,引入了 Remembered Set
|
1. 在回收 Region 时,为了不进行全堆的扫描,引入了 Remembered Set
|
||||||
2. Remembered Set 记录了当前 Region 中的对象被哪个对象引用了
|
2. Remembered Set 记录了当前 Region 中的对象被哪个对象引用了
|
||||||
@@ -748,7 +748,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节:
|
|||||||
2. 年轻代回收只回收Eden区和Survivor区
|
2. 年轻代回收只回收Eden区和Survivor区
|
||||||
3. YGC时,首先G1停止应用程序的执行(Stop-The-World),G1创建回收集(Collection Set),回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。
|
3. YGC时,首先G1停止应用程序的执行(Stop-The-World),G1创建回收集(Collection Set),回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0022.png">
|
||||||
|
|
||||||
图的大致意思就是:
|
图的大致意思就是:
|
||||||
|
|
||||||
@@ -804,7 +804,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节:
|
|||||||
|
|
||||||
当越来越多的对象晋升到老年代Old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region。这里需要注意:是一部分老年代,而不是全部老年代。可以选择哪些Old Region进行收集,从而可以对垃圾回收的耗时时间进行控制。也要注意的是Mixed GC并不是Full GC。
|
当越来越多的对象晋升到老年代Old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region。这里需要注意:是一部分老年代,而不是全部老年代。可以选择哪些Old Region进行收集,从而可以对垃圾回收的耗时时间进行控制。也要注意的是Mixed GC并不是Full GC。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0023.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -854,11 +854,11 @@ G1 GC的垃圾回收过程主要包括如下三个环节:
|
|||||||
|
|
||||||
截止JDK1.8,一共有7款不同的垃圾收集器。每一款的垃圾收集器都有不同的特点,在具体使用的时候,需要根据具体的情况选用不同的垃圾收集器。
|
截止JDK1.8,一共有7款不同的垃圾收集器。每一款的垃圾收集器都有不同的特点,在具体使用的时候,需要根据具体的情况选用不同的垃圾收集器。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0034.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0034.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0024.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0024.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -922,11 +922,11 @@ GC 日志分析
|
|||||||
|
|
||||||
2、这个只会显示总的GC堆的变化,如下:
|
2、这个只会显示总的GC堆的变化,如下:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0025.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0025.png">
|
||||||
|
|
||||||
3、参数解析
|
3、参数解析
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0026.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0026.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -938,11 +938,11 @@ GC 日志分析
|
|||||||
|
|
||||||
2、输入信息如下
|
2、输入信息如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0027.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0027.png">
|
||||||
|
|
||||||
3、参数解析
|
3、参数解析
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0028.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0028.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -954,7 +954,7 @@ GC 日志分析
|
|||||||
|
|
||||||
2、输出信息如下
|
2、输出信息如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0029.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0029.png">
|
||||||
|
|
||||||
3、说明:日志带上了日期和时间
|
3、说明:日志带上了日期和时间
|
||||||
|
|
||||||
@@ -989,13 +989,13 @@ GC 日志分析
|
|||||||
|
|
||||||
#### Young GC
|
#### Young GC
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0030.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0030.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Full GC
|
#### Full GC
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0031.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0031.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1029,11 +1029,11 @@ public class GCLogTest1 {
|
|||||||
|
|
||||||
1、首先我们会将3个2M的数组存放到Eden区,然后后面4M的数组来了后,将无法存储,因为Eden区只剩下2M的剩余空间了,那么将会进行一次Young GC操作,将原来Eden区的内容,存放到Survivor区,但是Survivor区也存放不下,那么就会直接晋级存入Old 区
|
1、首先我们会将3个2M的数组存放到Eden区,然后后面4M的数组来了后,将无法存储,因为Eden区只剩下2M的剩余空间了,那么将会进行一次Young GC操作,将原来Eden区的内容,存放到Survivor区,但是Survivor区也存放不下,那么就会直接晋级存入Old 区
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0032.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0032.png">
|
||||||
|
|
||||||
2、然后我们将4M对象存入到Eden区中
|
2、然后我们将4M对象存入到Eden区中
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0033.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0033.png">
|
||||||
|
|
||||||
老年代图画的有问题,free应该是4M
|
老年代图画的有问题,free应该是4M
|
||||||
|
|
||||||
@@ -1056,7 +1056,7 @@ Process finished with exit code 0
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0035.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0035.jpg">
|
||||||
|
|
||||||
与 JDK7 不同的是,JDK8 直接判定 4M 的数组为大对象,直接怼到老年区去了
|
与 JDK7 不同的是,JDK8 直接判定 4M 的数组为大对象,直接怼到老年区去了
|
||||||
|
|
||||||
@@ -1082,15 +1082,15 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
|
|||||||
|
|
||||||
在线分析网址:gceasy.io
|
在线分析网址:gceasy.io
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0036.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0036.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0037.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0037.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0038.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0038.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1126,7 +1126,7 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
|
|||||||
1. 停顿时间比其他几款收集器确实有了质的飞跃,但也未实现最大停顿时间控制在十毫秒以内的目标。
|
1. 停顿时间比其他几款收集器确实有了质的飞跃,但也未实现最大停顿时间控制在十毫秒以内的目标。
|
||||||
2. 而吞吐量方面出现了明显的下降,总运行时间是所有测试收集器里最长的。
|
2. 而吞吐量方面出现了明显的下降,总运行时间是所有测试收集器里最长的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0039.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0039.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1156,7 +1156,7 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
|
|||||||
|
|
||||||
**吞吐量**
|
**吞吐量**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0040.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0040.png">
|
||||||
|
|
||||||
max-JOPS:以低延迟为首要前提下的数据
|
max-JOPS:以低延迟为首要前提下的数据
|
||||||
|
|
||||||
@@ -1166,13 +1166,13 @@ critical-JOPS:不考虑低延迟下的数据
|
|||||||
|
|
||||||
**低延迟**
|
**低延迟**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0041.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0041.png">
|
||||||
|
|
||||||
在ZGC的强项停顿时间测试上,它毫不留情的将Parallel、G1拉开了两个数量级的差距。无论平均停顿、95%停顿、998停顿、99. 98停顿,还是最大停顿时间,ZGC都能毫不费劲控制在10毫秒以内。
|
在ZGC的强项停顿时间测试上,它毫不留情的将Parallel、G1拉开了两个数量级的差距。无论平均停顿、95%停顿、998停顿、99. 98停顿,还是最大停顿时间,ZGC都能毫不费劲控制在10毫秒以内。
|
||||||
|
|
||||||
虽然ZGC还在试验状态,没有完成所有特性,但此时性能已经相当亮眼,用“令人震惊、革命性”来形容,不为过。未来将在服务端、大内存、低延迟应用的首选垃圾收集器。
|
虽然ZGC还在试验状态,没有完成所有特性,但此时性能已经相当亮眼,用“令人震惊、革命性”来形容,不为过。未来将在服务端、大内存、低延迟应用的首选垃圾收集器。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0042.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0042.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1192,4 +1192,4 @@ critical-JOPS:不考虑低延迟下的数据
|
|||||||
|
|
||||||
AliGC是阿里巴巴JVM团队基于G1算法,面向大堆(LargeHeap)应用场景。指定场景下的对比:
|
AliGC是阿里巴巴JVM团队基于G1算法,面向大堆(LargeHeap)应用场景。指定场景下的对比:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0043.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0043.png">
|
||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第1章-JVM与Java体系结构。
|
description: JVM系列-第1章-JVM与Java体系结构。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 8c954c6
|
abbrlink: 8c954c6
|
||||||
date: 2020-11-02 11:51:56
|
date: 2020-11-02 11:51:56
|
||||||
---
|
---
|
||||||
@@ -38,7 +38,7 @@ date: 2020-11-02 11:51:56
|
|||||||
3. 新项目上线,对各种JVM参数设置一脸茫然,直接默认吧然后就JJ了。
|
3. 新项目上线,对各种JVM参数设置一脸茫然,直接默认吧然后就JJ了。
|
||||||
4. 每次面试之前都要重新背一遍JVM的一些原理概念性的东西,然而面试官却经常问你在实际项目中如何调优VM参数,如何解决GC、OOM等问题,一脸懵逼。
|
4. 每次面试之前都要重新背一遍JVM的一些原理概念性的东西,然而面试官却经常问你在实际项目中如何调优VM参数,如何解决GC、OOM等问题,一脸懵逼。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0001.png">
|
||||||
|
|
||||||
大部分Java开发人员,除了会在项目中使用到与Java平台相关的各种高精尖技术,对于Java技术的核心Java虚拟机了解甚少。
|
大部分Java开发人员,除了会在项目中使用到与Java平台相关的各种高精尖技术,对于Java技术的核心Java虚拟机了解甚少。
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ date: 2020-11-02 11:51:56
|
|||||||
1. 一些有一定工作经验的开发人员,打心眼儿里觉得SSM、微服务等上层技术才是重点,基础技术并不重要,这其实是一种本末倒置的“病态”。
|
1. 一些有一定工作经验的开发人员,打心眼儿里觉得SSM、微服务等上层技术才是重点,基础技术并不重要,这其实是一种本末倒置的“病态”。
|
||||||
2. 如果我们把核心类库的API比做数学公式的话,那么Java虚拟机的知识就好比公式的推导过程。
|
2. 如果我们把核心类库的API比做数学公式的话,那么Java虚拟机的知识就好比公式的推导过程。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0002.png">
|
||||||
|
|
||||||
- 计算机系统体系对我们来说越来越远,在不了解底层实现方式的前提下,通过高级语言很容易编写程序代码。但事实上计算机并不认识高级语言。
|
- 计算机系统体系对我们来说越来越远,在不了解底层实现方式的前提下,通过高级语言很容易编写程序代码。但事实上计算机并不认识高级语言。
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ Java VS C++
|
|||||||
1. 垃圾收集机制为我们打理了很多繁琐的工作,大大提高了开发的效率,但是,垃圾收集也不是万能的,懂得JVM内部的内存结构、工作机制,是设计高扩展性应用和诊断运行时问题的基础,也是Java工程师进阶的必备能力。
|
1. 垃圾收集机制为我们打理了很多繁琐的工作,大大提高了开发的效率,但是,垃圾收集也不是万能的,懂得JVM内部的内存结构、工作机制,是设计高扩展性应用和诊断运行时问题的基础,也是Java工程师进阶的必备能力。
|
||||||
2. C++语言需要程序员自己来分配内存和回收内存,对于高手来说可能更加舒服,但是对于普通开发者,如果技术实力不够,很容易造成内存泄漏。而Java全部交给JVM进行内存分配和回收,这也是一种趋势,减少程序员的工作量。
|
2. C++语言需要程序员自己来分配内存和回收内存,对于高手来说可能更加舒服,但是对于普通开发者,如果技术实力不够,很容易造成内存泄漏。而Java全部交给JVM进行内存分配和回收,这也是一种趋势,减少程序员的工作量。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0003.png">
|
||||||
|
|
||||||
## 什么人需要学JVM?
|
## 什么人需要学JVM?
|
||||||
|
|
||||||
@@ -103,26 +103,26 @@ Java VS C++
|
|||||||
|
|
||||||
**英文文档规范**:https://docs.oracle.com/javase/specs/index.html
|
**英文文档规范**:https://docs.oracle.com/javase/specs/index.html
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0004.png">
|
||||||
|
|
||||||
**中文书籍:**
|
**中文书籍:**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0005.png">
|
||||||
|
|
||||||
> 周志明老师的这本书**非常推荐看**,不过只推荐看第三版,第三版较第二版更新了很多,个人觉得没必要再看第二版。
|
> 周志明老师的这本书**非常推荐看**,不过只推荐看第三版,第三版较第二版更新了很多,个人觉得没必要再看第二版。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0007.png">
|
||||||
|
|
||||||
TIOBE排行榜
|
TIOBE排行榜
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
**TIOBE 排行榜**:https://www.tiobe.com/tiobe-index/
|
**TIOBE 排行榜**:https://www.tiobe.com/tiobe-index/
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0008.png">
|
||||||
|
|
||||||
- 世界上没有最好的编程语言,只有最适用于具体应用场景的编程语言。
|
- 世界上没有最好的编程语言,只有最适用于具体应用场景的编程语言。
|
||||||
- 目前网上一直流传Java被python,go撼动Java第一的地位。学习者不需要太担心,Java强大的生态圈,也不是说是朝夕之间可以被撼动的。
|
- 目前网上一直流传Java被python,go撼动Java第一的地位。学习者不需要太担心,Java强大的生态圈,也不是说是朝夕之间可以被撼动的。
|
||||||
@@ -148,14 +148,14 @@ Java-跨平台的语言
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0009.png">
|
||||||
|
|
||||||
JVM-跨语言的平台
|
JVM-跨语言的平台
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ JVM-跨语言的平台
|
|||||||
2. 自己动手写一个Java虚拟机,难吗?
|
2. 自己动手写一个Java虚拟机,难吗?
|
||||||
3. 天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣
|
3. 天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0011.png">
|
||||||
|
|
||||||
Java发展重大事件
|
Java发展重大事件
|
||||||
------------
|
------------
|
||||||
@@ -215,7 +215,7 @@ Java发展重大事件
|
|||||||
|
|
||||||
## Open JDK和Oracle JDK
|
## Open JDK和Oracle JDK
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0012.png">
|
||||||
|
|
||||||
- 在JDK11之前,Oracle JDK中还会存在一些Open JDK中没有的,闭源的功能。但在JDK11中,我们可以认为Open JDK和Oracle JDK代码实质上已经达到完全一致的程度了。
|
- 在JDK11之前,Oracle JDK中还会存在一些Open JDK中没有的,闭源的功能。但在JDK11中,我们可以认为Open JDK和Oracle JDK代码实质上已经达到完全一致的程度了。
|
||||||
- 主要的区别就是两者更新周期不一样
|
- 主要的区别就是两者更新周期不一样
|
||||||
@@ -258,11 +258,11 @@ JVM的位置
|
|||||||
|
|
||||||
JVM是运行在操作系统之上的,它与硬件没有直接的交互
|
JVM是运行在操作系统之上的,它与硬件没有直接的交互
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0014.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ JVM的整体结构
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ Java代码执行流程
|
|||||||
|
|
||||||
凡是能生成被Java虚拟机所能解释、运行的字节码文件,那么理论上我们就可以自己设计一套语言了
|
凡是能生成被Java虚拟机所能解释、运行的字节码文件,那么理论上我们就可以自己设计一套语言了
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0016.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第2章-类加载子系统。
|
description: JVM系列-第2章-类加载子系统。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 2e0079af
|
abbrlink: 2e0079af
|
||||||
date: 2020-11-02 21:31:58
|
date: 2020-11-02 21:31:58
|
||||||
---
|
---
|
||||||
@@ -25,19 +25,19 @@ date: 2020-11-02 21:31:58
|
|||||||
|
|
||||||
### 简图
|
### 简图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0001.png">
|
||||||
|
|
||||||
### 详细图
|
### 详细图
|
||||||
|
|
||||||
英文版
|
英文版
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0002.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0002.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
中文版
|
中文版
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0003.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0003.jpg">
|
||||||
|
|
||||||
注意:方法区只有HotSpot虚拟机有,J9,JRockit都没有
|
注意:方法区只有HotSpot虚拟机有,J9,JRockit都没有
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ date: 2020-11-02 21:31:58
|
|||||||
|
|
||||||
3. **加载的类信息存放于一块称为方法区的内存空间**。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
|
3. **加载的类信息存放于一块称为方法区的内存空间**。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ date: 2020-11-02 21:31:58
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0005.png">
|
||||||
|
|
||||||
类加载过程
|
类加载过程
|
||||||
-------
|
-------
|
||||||
@@ -96,11 +96,11 @@ public class HelloLoader {
|
|||||||
* 加载成功,则进行链接、初始化等操作。完成后调用 HelloLoader 类中的静态方法 main
|
* 加载成功,则进行链接、初始化等操作。完成后调用 HelloLoader 类中的静态方法 main
|
||||||
* 加载失败则抛出异常
|
* 加载失败则抛出异常
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0006.png">
|
||||||
|
|
||||||
完整的流程图如下所示:
|
完整的流程图如下所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ public class HelloLoader {
|
|||||||
|
|
||||||
使用 BinaryViewer软件查看字节码文件,其开头均为 CAFE BABE ,如果出现不合法的字节码文件,那么将会验证不通过。
|
使用 BinaryViewer软件查看字节码文件,其开头均为 CAFE BABE ,如果出现不合法的字节码文件,那么将会验证不通过。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0008.png">
|
||||||
|
|
||||||
#### 准备(Prepare)
|
#### 准备(Prepare)
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ public class HelloApp {
|
|||||||
|
|
||||||
* 反编译 class 文件后可以查看符号引用,下面带# 的就是符号引用
|
* 反编译 class 文件后可以查看符号引用,下面带# 的就是符号引用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0023.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0023.png"/>
|
||||||
|
|
||||||
### 初始化阶段
|
### 初始化阶段
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ public class HelloApp {
|
|||||||
|
|
||||||
查看下面这个代码的字节码,可以发现有一个`<clinit>()`方法。
|
查看下面这个代码的字节码,可以发现有一个`<clinit>()`方法。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0009.png">
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class ClassInitTest {
|
public class ClassInitTest {
|
||||||
@@ -277,15 +277,15 @@ public class ClassInitTest {
|
|||||||
|
|
||||||
**举例2:无 static 变量**
|
**举例2:无 static 变量**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0010.png">
|
||||||
|
|
||||||
加上之后就有了
|
加上之后就有了
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0011.png">
|
||||||
|
|
||||||
#### 4说明
|
#### 4说明
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0012.png">
|
||||||
|
|
||||||
在构造器中:
|
在构造器中:
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ public class ClassInitTest {
|
|||||||
|
|
||||||
若该类具有父类,JVM会保证子类的`<clinit>()`执行前,父类的`<clinit>()`已经执行完毕
|
若该类具有父类,JVM会保证子类的`<clinit>()`执行前,父类的`<clinit>()`已经执行完毕
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0013.png">
|
||||||
|
|
||||||
如上代码,加载流程如下:
|
如上代码,加载流程如下:
|
||||||
|
|
||||||
@@ -374,17 +374,17 @@ class DeadThread{
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0014.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**ExtClassLoader**
|
**ExtClassLoader**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0015.png">
|
||||||
|
|
||||||
**AppClassLoader**
|
**AppClassLoader**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0016.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -579,15 +579,15 @@ public class CustomClassLoader extends ClassLoader {
|
|||||||
|
|
||||||
ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)
|
ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0017.png">
|
||||||
|
|
||||||
sun.misc.Launcher 它是一个java虚拟机的入口应用
|
sun.misc.Launcher 它是一个java虚拟机的入口应用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0018.png">
|
||||||
|
|
||||||
#### 获取ClassLoader途径
|
#### 获取ClassLoader途径
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0019.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -640,7 +640,7 @@ Java虚拟机对class文件采用的是**按需加载**的方式,也就是说
|
|||||||
3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
|
3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
|
||||||
4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常
|
4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0020.png">
|
||||||
|
|
||||||
### 双亲委派机制代码演示
|
### 双亲委派机制代码演示
|
||||||
|
|
||||||
@@ -707,7 +707,7 @@ public class String {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0021.png">
|
||||||
|
|
||||||
由于双亲委派机制一直找父类,所以最后找到了Bootstrap ClassLoader,Bootstrap ClassLoader找到的是 JDK 自带的 String 类,在那个String类中并没有 main() 方法,所以就报了上面的错误。
|
由于双亲委派机制一直找父类,所以最后找到了Bootstrap ClassLoader,Bootstrap ClassLoader找到的是 JDK 自带的 String 类,在那个String类中并没有 main() 方法,所以就报了上面的错误。
|
||||||
|
|
||||||
@@ -765,7 +765,7 @@ Process finished with exit code 1
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0022.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第3章-运行时数据区。
|
description: JVM系列-第3章-运行时数据区。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: a7ad3cab
|
abbrlink: a7ad3cab
|
||||||
date: 2020-11-09 15:38:42
|
date: 2020-11-09 15:38:42
|
||||||
---
|
---
|
||||||
@@ -27,15 +27,15 @@ date: 2020-11-09 15:38:42
|
|||||||
|
|
||||||
本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段
|
本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0001.png">
|
||||||
|
|
||||||
当我们通过前面的:类的加载 --> 验证 --> 准备 --> 解析 --\> 初始化,这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区
|
当我们通过前面的:类的加载 --> 验证 --> 准备 --> 解析 --\> 初始化,这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0002.png">
|
||||||
|
|
||||||
类比一下也就是大厨做饭,我们把大厨后面的东西(切好的菜,刀,调料),比作是运行时数据区。而厨师可以类比于执行引擎,将通过准备的东西进行制作成精美的菜品。
|
类比一下也就是大厨做饭,我们把大厨后面的东西(切好的菜,刀,调料),比作是运行时数据区。而厨师可以类比于执行引擎,将通过准备的东西进行制作成精美的菜品。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ date: 2020-11-09 15:38:42
|
|||||||
|
|
||||||
> 下图来自阿里巴巴手册JDK8
|
> 下图来自阿里巴巴手册JDK8
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0004.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0004.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ date: 2020-11-09 15:38:42
|
|||||||
- 线程独有:独立包括程序计数器、栈、本地方法栈
|
- 线程独有:独立包括程序计数器、栈、本地方法栈
|
||||||
- 线程间共享:堆、堆外内存(永久代或元空间、代码缓存)
|
- 线程间共享:堆、堆外内存(永久代或元空间、代码缓存)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ date: 2020-11-09 15:38:42
|
|||||||
|
|
||||||
**每个JVM只有一个Runtime实例**。即为运行时环境,相当于内存结构的中间的那个框框:运行时环境。
|
**每个JVM只有一个Runtime实例**。即为运行时环境,相当于内存结构的中间的那个框框:运行时环境。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ PC寄存器介绍
|
|||||||
|
|
||||||
> 官方文档网址:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
|
> 官方文档网址:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0007.png">
|
||||||
|
|
||||||
1. JVM中的程序计数寄存器(Program Counter Register)中,Register的命名源于CPU的寄存器,**寄存器存储指令相关的现场信息**。CPU只有把数据装载到寄存器才能够运行。
|
1. JVM中的程序计数寄存器(Program Counter Register)中,Register的命名源于CPU的寄存器,**寄存器存储指令相关的现场信息**。CPU只有把数据装载到寄存器才能够运行。
|
||||||
2. 这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。**JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟**。
|
2. 这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。**JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟**。
|
||||||
@@ -132,7 +132,7 @@ PC寄存器介绍
|
|||||||
|
|
||||||
PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令,并执行该指令。
|
PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令,并执行该指令。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0008.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ SourceFile: "PCRegisterTest.java"
|
|||||||
|
|
||||||
* 左边的数字代表**指令地址(指令偏移)**,即 PC 寄存器中可能存储的值,然后执行引擎读取 PC 寄存器中的值,并执行该指令
|
* 左边的数字代表**指令地址(指令偏移)**,即 PC 寄存器中可能存储的值,然后执行引擎读取 PC 寄存器中的值,并执行该指令
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ SourceFile: "PCRegisterTest.java"
|
|||||||
2. JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令
|
2. JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ CPU 时间片
|
|||||||
3. 但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,**每个程序轮流执行**。
|
3. 但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,**每个程序轮流执行**。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0011.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ CPU 时间片
|
|||||||
|
|
||||||
## 本地方法
|
## 本地方法
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ Java使用起来非常方便,然而有些层次的任务用Java实现起来不
|
|||||||
4. 本地方法一般是使用C语言或C++语言实现的。
|
4. 本地方法一般是使用C语言或C++语言实现的。
|
||||||
5. 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。
|
5. 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第4章-虚拟机栈。
|
description: JVM系列-第4章-虚拟机栈。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 5b1b6560
|
abbrlink: 5b1b6560
|
||||||
date: 2020-11-10 10:38:42
|
date: 2020-11-10 10:38:42
|
||||||
---
|
---
|
||||||
@@ -31,7 +31,7 @@ date: 2020-11-10 10:38:42
|
|||||||
1. 首先栈是运行时的单位,而堆是存储的单位。
|
1. 首先栈是运行时的单位,而堆是存储的单位。
|
||||||
2. 即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放哪里
|
2. 即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放哪里
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ date: 2020-11-10 10:38:42
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0002.png">
|
||||||
|
|
||||||
- 虚拟机栈的生命周期
|
- 虚拟机栈的生命周期
|
||||||
- 生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了
|
- 生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了
|
||||||
@@ -90,7 +90,7 @@ date: 2020-11-10 10:38:42
|
|||||||
- 对于栈来说不存在垃圾回收问题
|
- 对于栈来说不存在垃圾回收问题
|
||||||
- 栈不需要GC,但是可能存在OOM
|
- 栈不需要GC,但是可能存在OOM
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0003.png">
|
||||||
|
|
||||||
### 虚拟机栈的异常
|
### 虚拟机栈的异常
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ Exception in thread "main" java.lang.StackOverflowError
|
|||||||
|
|
||||||
**设置栈参数之后**
|
**设置栈参数之后**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0004.png">
|
||||||
|
|
||||||
部分输出结果
|
部分输出结果
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ Exception in thread "main" java.lang.StackOverflowError
|
|||||||
4. 如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
|
4. 如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0005.png">
|
||||||
|
|
||||||
1. **不同线程中所包含的栈帧是不允许存在相互引用的**,即不可能在一个栈帧之中引用另外一个线程的栈帧。
|
1. **不同线程中所包含的栈帧是不允许存在相互引用的**,即不可能在一个栈帧之中引用另外一个线程的栈帧。
|
||||||
2. 如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
|
2. 如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
|
||||||
@@ -230,11 +230,11 @@ Exception in thread "main" java.lang.StackOverflowError
|
|||||||
|
|
||||||
- 一些附加信息
|
- 一些附加信息
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0006.png">
|
||||||
|
|
||||||
并行每个线程下的栈都是私有的,因此每个线程都有自己各自的栈,并且每个栈里面都有很多栈帧,栈帧的大小主要由局部变量表 和 操作数栈决定的
|
并行每个线程下的栈都是私有的,因此每个线程都有自己各自的栈,并且每个栈里面都有很多栈帧,栈帧的大小主要由局部变量表 和 操作数栈决定的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0007.png">
|
||||||
|
|
||||||
局部变量表
|
局部变量表
|
||||||
-------
|
-------
|
||||||
@@ -312,7 +312,7 @@ public class LocalVariablesTest {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0008.png">
|
||||||
|
|
||||||
看完字节码后,可得结论:所以局部变量表所需的容量大小是在编译期确定下来的。
|
看完字节码后,可得结论:所以局部变量表所需的容量大小是在编译期确定下来的。
|
||||||
|
|
||||||
@@ -324,29 +324,29 @@ public class LocalVariablesTest {
|
|||||||
|
|
||||||
1、0-15 也就是有16行字节码
|
1、0-15 也就是有16行字节码
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2、方法异常信息表
|
2、方法异常信息表
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0010.png">
|
||||||
|
|
||||||
3、Misc
|
3、Misc
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0011.png">
|
||||||
|
|
||||||
4、行号表
|
4、行号表
|
||||||
|
|
||||||
Java代码的行号和字节码指令行号的对应关系
|
Java代码的行号和字节码指令行号的对应关系
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5、注意:生效行数和剩余有效行数都是针对于字节码文件的行数
|
5、注意:生效行数和剩余有效行数都是针对于字节码文件的行数
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0013.png">
|
||||||
|
|
||||||
1、图中圈的东西表示该局部变量的作用域
|
1、图中圈的东西表示该局部变量的作用域
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ Java代码的行号和字节码指令行号的对应关系
|
|||||||
6. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可。(比如:访问long或double类型变量)
|
6. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可。(比如:访问long或double类型变量)
|
||||||
7. 如果当前帧是由构造方法或者实例方法创建的,那么**该对象引用this将会存放在index为0的slot处**,其余的参数按照参数表顺序继续排列。(this也相当于一个变量)
|
7. 如果当前帧是由构造方法或者实例方法创建的,那么**该对象引用this将会存放在index为0的slot处**,其余的参数按照参数表顺序继续排列。(this也相当于一个变量)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0014.png">
|
||||||
|
|
||||||
### Slot代码示例
|
### Slot代码示例
|
||||||
|
|
||||||
@@ -388,7 +388,7 @@ Java代码的行号和字节码指令行号的对应关系
|
|||||||
|
|
||||||
局部变量表:this 存放在 index = 0 的位置
|
局部变量表:this 存放在 index = 0 的位置
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ Java代码的行号和字节码指令行号的对应关系
|
|||||||
|
|
||||||
weight 为 double 类型,index 直接从 3 蹦到了 5
|
weight 为 double 类型,index 直接从 3 蹦到了 5
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0016.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
|
|||||||
|
|
||||||
局部变量 c 重用了局部变量 b 的 slot 位置
|
局部变量 c 重用了局部变量 b 的 slot 位置
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0017.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -499,13 +499,13 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
|
|||||||
- 比如:执行复制、交换、求和等操作
|
- 比如:执行复制、交换、求和等操作
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0018.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0019.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0020.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0020.jpg">
|
||||||
|
|
||||||
局部变量表就相当于食材
|
局部变量表就相当于食材
|
||||||
|
|
||||||
@@ -571,23 +571,23 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
|
|||||||
10 return
|
10 return
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0021.png">
|
||||||
|
|
||||||
### 一步一步看流程
|
### 一步一步看流程
|
||||||
|
|
||||||
1、首先执行第一条语句,PC寄存器指向的是0,也就是指令地址为0,然后使用bipush让操作数15入操作数栈。
|
1、首先执行第一条语句,PC寄存器指向的是0,也就是指令地址为0,然后使用bipush让操作数15入操作数栈。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0022.png">
|
||||||
|
|
||||||
2、执行完后,PC寄存器往下移,指向下一行代码,下一行代码就是将操作数栈的元素存储到局部变量表1的位置(istore_1),我们可以看到局部变量表的已经增加了一个元素。并且操作数栈为空了
|
2、执行完后,PC寄存器往下移,指向下一行代码,下一行代码就是将操作数栈的元素存储到局部变量表1的位置(istore_1),我们可以看到局部变量表的已经增加了一个元素。并且操作数栈为空了
|
||||||
|
|
||||||
* 解释为什么局部变量表索引从 1 开始,因为该方法为实例方法,局部变量表索引为 0 的位置存放的是 this
|
* 解释为什么局部变量表索引从 1 开始,因为该方法为实例方法,局部变量表索引为 0 的位置存放的是 this
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0023.png">
|
||||||
|
|
||||||
3、然后PC下移,指向的是下一行。让操作数8也入栈,同时执行store操作,存入局部变量表中
|
3、然后PC下移,指向的是下一行。让操作数8也入栈,同时执行store操作,存入局部变量表中
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0024.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0024.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -595,11 +595,11 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
|
|||||||
|
|
||||||
iload_1:取出局部变量表中索引为1的数据入操作数栈
|
iload_1:取出局部变量表中索引为1的数据入操作数栈
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0025.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0025.png">
|
||||||
|
|
||||||
5、然后将操作数栈中的两个元素执行相加操作,并存储在局部变量表3的位置
|
5、然后将操作数栈中的两个元素执行相加操作,并存储在局部变量表3的位置
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0026.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0026.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -607,7 +607,7 @@ iload_1:取出局部变量表中索引为1的数据入操作数栈
|
|||||||
|
|
||||||
**关于类型转换的说明**
|
**关于类型转换的说明**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0027.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0027.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -616,7 +616,7 @@ iload_1:取出局部变量表中索引为1的数据入操作数栈
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0028.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0028.png">
|
||||||
|
|
||||||
- m改成800之后,byte存储不了,就成了short型,sipush 800
|
- m改成800之后,byte存储不了,就成了short型,sipush 800
|
||||||
|
|
||||||
@@ -645,11 +645,11 @@ iload_1:取出局部变量表中索引为1的数据入操作数栈
|
|||||||
|
|
||||||
getSum() 方法字节码指令:最后带着个 ireturn
|
getSum() 方法字节码指令:最后带着个 ireturn
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0029.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0029.png">
|
||||||
|
|
||||||
testGetSum() 方法字节码指令:一上来就加载 getSum() 方法的返回值()
|
testGetSum() 方法字节码指令:一上来就加载 getSum() 方法的返回值()
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0030.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0030.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -841,7 +841,7 @@ SourceFile: "DynamicLinkingTest.java"
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0031.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0031.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1136,7 +1136,7 @@ interface MethodInterface {
|
|||||||
|
|
||||||
Son 类中 show() 方法的字节码指令如下
|
Son 类中 show() 方法的字节码指令如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0032.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0032.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1176,7 +1176,7 @@ public class Lambda {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0033.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0033.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1233,7 +1233,7 @@ Java:String info = "mogu blog"; (Java是静态类型语言的,会先
|
|||||||
|
|
||||||
如图所示:如果类中重写了方法,那么调用的时候,就会直接在该类的虚方法表中查找
|
如图所示:如果类中重写了方法,那么调用的时候,就会直接在该类的虚方法表中查找
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0034.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0034.png">
|
||||||
|
|
||||||
1、比如说son在调用toString的时候,Son没有重写过,Son的父类Father也没有重写过,那就直接调用Object类的toString。那么就直接在虚方法表里指明toString直接指向Object类。
|
1、比如说son在调用toString的时候,Son没有重写过,Son的父类Father也没有重写过,那就直接调用Object类的toString。那么就直接在虚方法表里指明toString直接指向Object类。
|
||||||
|
|
||||||
@@ -1243,24 +1243,24 @@ Java:String info = "mogu blog"; (Java是静态类型语言的,会先
|
|||||||
|
|
||||||
**例子2**
|
**例子2**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0035.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0035.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0036.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0036.jpg">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0037.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0037.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0038.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0038.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
方法返回地址
|
方法返回地址
|
||||||
--------
|
--------
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0039.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0039.png">
|
||||||
|
|
||||||
> 在一些帖子里,方法返回地址、动态链接、一些附加信息 也叫做帧数据区
|
> 在一些帖子里,方法返回地址、动态链接、一些附加信息 也叫做帧数据区
|
||||||
|
|
||||||
@@ -1309,7 +1309,7 @@ Java:String info = "mogu blog"; (Java是静态类型语言的,会先
|
|||||||
2. 方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码
|
2. 方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0040.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0040.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1321,7 +1321,7 @@ Java:String info = "mogu blog"; (Java是静态类型语言的,会先
|
|||||||
* target :出现异常跳转至地址为 11 的指令执行
|
* target :出现异常跳转至地址为 11 的指令执行
|
||||||
* type :捕获异常的类型
|
* type :捕获异常的类型
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0041.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0041.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第5章-堆。
|
description: JVM系列-第5章-堆。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 50ac3a1c
|
abbrlink: 50ac3a1c
|
||||||
date: 2020-11-11 20:38:42
|
date: 2020-11-11 20:38:42
|
||||||
---
|
---
|
||||||
@@ -27,7 +27,7 @@ date: 2020-11-11 20:38:42
|
|||||||
1. 堆针对一个JVM进程来说是唯一的。也就是**一个进程只有一个JVM实例**,一个JVM实例中就有一个运行时数据区,一个运行时数据区只有一个堆和一个方法区。
|
1. 堆针对一个JVM进程来说是唯一的。也就是**一个进程只有一个JVM实例**,一个JVM实例中就有一个运行时数据区,一个运行时数据区只有一个堆和一个方法区。
|
||||||
2. 但是**进程包含多个线程,他们是共享同一堆空间的**。
|
2. 但是**进程包含多个线程,他们是共享同一堆空间的**。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class SimpleHeap {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -88,13 +88,13 @@ public class SimpleHeap {
|
|||||||
|
|
||||||
约定:新生区 <–> 新生代 <–> 年轻代 、 养老区 <–> 老年区 <–> 老年代、 永久区 <–\> 永久代
|
约定:新生区 <–> 新生代 <–> 年轻代 、 养老区 <–> 老年区 <–> 老年代、 永久区 <–\> 永久代
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. 堆空间内部结构,JDK1.8之前从永久代 替换成 元空间
|
2. 堆空间内部结构,JDK1.8之前从永久代 替换成 元空间
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -122,17 +122,17 @@ public class HeapDemo {
|
|||||||
|
|
||||||
1、双击jdk目录下的这个文件
|
1、双击jdk目录下的这个文件
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2、工具 -> 插件 -> 安装Visual GC插件
|
2、工具 -> 插件 -> 安装Visual GC插件
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0006.png">
|
||||||
|
|
||||||
3、运行上面的代码
|
3、运行上面的代码
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ public class HeapSpaceInitial {
|
|||||||
|
|
||||||
设置下参数再看
|
设置下参数再看
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0008.png">
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class HeapSpaceInitial {
|
public class HeapSpaceInitial {
|
||||||
@@ -250,7 +250,7 @@ public class HeapSpaceInitial {
|
|||||||
|
|
||||||
**方式一: jps / jstat -gc 进程id**
|
**方式一: jps / jstat -gc 进程id**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0009.png">
|
||||||
|
|
||||||
> jps:查看java进程
|
> jps:查看java进程
|
||||||
>
|
>
|
||||||
@@ -285,7 +285,7 @@ OU: 老年代使用的量
|
|||||||
|
|
||||||
**方式二:-XX:+PrintGCDetails**
|
**方式二:-XX:+PrintGCDetails**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -333,11 +333,11 @@ Process finished with exit code 1
|
|||||||
|
|
||||||
2、堆内存变化图
|
2、堆内存变化图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0011.png">
|
||||||
|
|
||||||
3、原因:大对象导致堆内存溢出
|
3、原因:大对象导致堆内存溢出
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -357,9 +357,9 @@ Process finished with exit code 1
|
|||||||
|
|
||||||
3、其中年轻代又可以划分为Eden空间、Survivor0空间和Survivor1空间(有时也叫做from区、to区)
|
3、其中年轻代又可以划分为Eden空间、Survivor0空间和Survivor1空间(有时也叫做from区、to区)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0013.png">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0014.png">
|
||||||
|
|
||||||
- 配置新生代与老年代在堆结构的占比
|
- 配置新生代与老年代在堆结构的占比
|
||||||
|
|
||||||
@@ -383,7 +383,7 @@ Process finished with exit code 1
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -439,7 +439,7 @@ public class EdenSurvivorTest {
|
|||||||
|
|
||||||
1、我们创建的对象,一般都是存放在Eden区的,**当我们Eden区满了后,就会触发GC操作**,一般被称为 YGC / Minor GC操作
|
1、我们创建的对象,一般都是存放在Eden区的,**当我们Eden区满了后,就会触发GC操作**,一般被称为 YGC / Minor GC操作
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0016.png">
|
||||||
|
|
||||||
2、当我们进行一次垃圾收集后,红色的对象将会被回收,而绿色的独享还被占用着,存放在S0(Survivor From)区。同时我们给每个对象设置了一个年龄计数器,经过一次回收后还存在的对象,将其年龄加 1。
|
2、当我们进行一次垃圾收集后,红色的对象将会被回收,而绿色的独享还被占用着,存放在S0(Survivor From)区。同时我们给每个对象设置了一个年龄计数器,经过一次回收后还存在的对象,将其年龄加 1。
|
||||||
|
|
||||||
@@ -453,11 +453,11 @@ public class EdenSurvivorTest {
|
|||||||
>
|
>
|
||||||
> 3、也就是说s0区和s1区在互相转换。
|
> 3、也就是说s0区和s1区在互相转换。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0017.png">
|
||||||
|
|
||||||
4、我们继续不断的进行对象生成和垃圾回收,当Survivor中的对象的年龄达到15的时候,将会触发一次 Promotion 晋升的操作,也就是将年轻代中的对象晋升到老年代中
|
4、我们继续不断的进行对象生成和垃圾回收,当Survivor中的对象的年龄达到15的时候,将会触发一次 Promotion 晋升的操作,也就是将年轻代中的对象晋升到老年代中
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0018.png">
|
||||||
|
|
||||||
关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。
|
关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。
|
||||||
|
|
||||||
@@ -475,7 +475,7 @@ public class EdenSurvivorTest {
|
|||||||
* 那万一老年代都放不下,则先触发FullGC ,再看看能不能放下,放得下最好,但如果还是放不下,那只能报 OOM
|
* 那万一老年代都放不下,则先触发FullGC ,再看看能不能放下,放得下最好,但如果还是放不下,那只能报 OOM
|
||||||
3. 如果 Eden 区满了,将对象往幸存区拷贝时,发现幸存区放不下啦,那只能便宜了某些新对象,让他们直接晋升至老年区
|
3. 如果 Eden 区满了,将对象往幸存区拷贝时,发现幸存区放不下啦,那只能便宜了某些新对象,让他们直接晋升至老年区
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0019.png">
|
||||||
|
|
||||||
### 常用调优工具
|
### 常用调优工具
|
||||||
|
|
||||||
@@ -528,7 +528,7 @@ GC分类
|
|||||||
3. Minor GC会引发STW(Stop The World),暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
|
3. Minor GC会引发STW(Stop The World),暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0020.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -649,7 +649,7 @@ Heap
|
|||||||
* 新生代:有Eden、两块大小相同的survivor(又称为from/to或s0/s1)构成,to总为空。
|
* 新生代:有Eden、两块大小相同的survivor(又称为from/to或s0/s1)构成,to总为空。
|
||||||
* 老年代:存放新生代中经历多次GC仍然存活的对象。
|
* 老年代:存放新生代中经历多次GC仍然存活的对象。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0021.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -661,7 +661,7 @@ Heap
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0022.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -711,7 +711,7 @@ TLAB(Thread Local Allocation Buffer)
|
|||||||
2. 多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为**快速分配策略**。
|
2. 多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为**快速分配策略**。
|
||||||
3. 据我所知所有OpenJDK衍生出来的JVM都提供了TLAB的设计。
|
3. 据我所知所有OpenJDK衍生出来的JVM都提供了TLAB的设计。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0023.png">
|
||||||
|
|
||||||
1、每个线程都有一个TLAB空间
|
1、每个线程都有一个TLAB空间
|
||||||
|
|
||||||
@@ -741,7 +741,7 @@ TLAB(Thread Local Allocation Buffer)
|
|||||||
|
|
||||||
**TLAB 分配过程**
|
**TLAB 分配过程**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0024.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0024.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第6章-方法区。
|
description: JVM系列-第6章-方法区。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 136cd965
|
abbrlink: 136cd965
|
||||||
date: 2020-11-13 19:38:42
|
date: 2020-11-13 19:38:42
|
||||||
---
|
---
|
||||||
@@ -24,7 +24,7 @@ date: 2020-11-13 19:38:42
|
|||||||
|
|
||||||
ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场景就是数据库连接管理,以及会话管理。
|
ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场景就是数据库连接管理,以及会话管理。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0001.png">
|
||||||
|
|
||||||
**栈、堆、方法区的交互关系**
|
**栈、堆、方法区的交互关系**
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场
|
|||||||
3. 真正的 person 对象存放在 Java 堆中
|
3. 真正的 person 对象存放在 Java 堆中
|
||||||
4. 在 person 对象中,有个指针指向方法区中的 person 类型数据,表明这个 person 对象是用方法区中的 Person 类 new 出来的
|
4. 在 person 对象中,有个指针指向方法区中的 person 类型数据,表明这个 person 对象是用方法区中的 Person 类 new 出来的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0002.png">
|
||||||
|
|
||||||
方法区的理解
|
方法区的理解
|
||||||
--------
|
--------
|
||||||
@@ -47,7 +47,7 @@ ThreadLocal:如何保证多个线程在并发环境下的安全性?典型场
|
|||||||
1. 《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
|
1. 《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
|
||||||
3. 所以,**方法区可以看作是一块独立于Java堆的内存空间**。
|
3. 所以,**方法区可以看作是一块独立于Java堆的内存空间**。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ public class MethodAreaDemo {
|
|||||||
|
|
||||||
简单的程序,加载了1600多个类
|
简单的程序,加载了1600多个类
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ public class MethodAreaDemo {
|
|||||||
5. 永久代、元空间二者并不只是名字变了,内部结构也调整了
|
5. 永久代、元空间二者并不只是名字变了,内部结构也调整了
|
||||||
6. 根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常
|
6. 根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ public class MethodAreaDemo {
|
|||||||
2. -XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M
|
2. -XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M
|
||||||
3. 当JVM加载的类信息容量超过了这个值,会报异常OutofMemoryError:PermGen space。
|
3. 当JVM加载的类信息容量超过了这个值,会报异常OutofMemoryError:PermGen space。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0006.png">
|
||||||
|
|
||||||
### JDK8及以后(元空间)
|
### JDK8及以后(元空间)
|
||||||
|
|
||||||
@@ -228,11 +228,11 @@ Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
|
|||||||
|
|
||||||
#### 概念
|
#### 概念
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0007.png">
|
||||||
|
|
||||||
《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:它用于存储已被虚拟机加载的**类型信息、常量、静态变量、即时编译器编译后的代码缓存**等。
|
《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:它用于存储已被虚拟机加载的**类型信息、常量、静态变量、即时编译器编译后的代码缓存**等。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0008.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -714,7 +714,7 @@ public static int count;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0009.png">
|
||||||
|
|
||||||
1. 方法区,内部包含了运行时常量池
|
1. 方法区,内部包含了运行时常量池
|
||||||
2. 字节码文件,内部包含了常量池。(之前的字节码文件中已经看到了很多Constant pool的东西,这个就是常量池)
|
2. 字节码文件,内部包含了常量池。(之前的字节码文件中已经看到了很多Constant pool的东西,这个就是常量池)
|
||||||
@@ -728,7 +728,7 @@ public static int count;
|
|||||||
1. 一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外。还包含一项信息就是**常量池表**(**Constant Pool Table**),包括各种字面量和对类型、域和方法的符号引用。
|
1. 一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外。还包含一项信息就是**常量池表**(**Constant Pool Table**),包括各种字面量和对类型、域和方法的符号引用。
|
||||||
2. 字面量: 10 , “我是某某”这种数字和字符串都是字面量
|
2. 字面量: 10 , “我是某某”这种数字和字符串都是字面量
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0010.png">
|
||||||
|
|
||||||
**为什么需要常量池?**
|
**为什么需要常量池?**
|
||||||
|
|
||||||
@@ -749,7 +749,7 @@ public static int count;
|
|||||||
2. 比如说我们这个文件中有6个地方用到了"hello"这个字符串,如果不用常量池,就需要在6个地方全写一遍,造成臃肿。我们可以将"hello"等所需用到的结构信息记录在常量池中,并通过**引用的方式**,来加载、调用所需的结构
|
2. 比如说我们这个文件中有6个地方用到了"hello"这个字符串,如果不用常量池,就需要在6个地方全写一遍,造成臃肿。我们可以将"hello"等所需用到的结构信息记录在常量池中,并通过**引用的方式**,来加载、调用所需的结构
|
||||||
4. 这里的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。
|
4. 这里的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0011.png">
|
||||||
|
|
||||||
**常量池中有啥?**
|
**常量池中有啥?**
|
||||||
|
|
||||||
@@ -924,69 +924,69 @@ SourceFile: "MethodAreaDemo.java"
|
|||||||
|
|
||||||
1、初始状态
|
1、初始状态
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0012.png">
|
||||||
|
|
||||||
2、首先将操作数500压入操作数栈中
|
2、首先将操作数500压入操作数栈中
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0013.png">
|
||||||
|
|
||||||
3、然后操作数 500 从操作数栈中取出,存储到局部变量表中索引为 1 的位置
|
3、然后操作数 500 从操作数栈中取出,存储到局部变量表中索引为 1 的位置
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0014.png">
|
||||||
|
|
||||||
4、
|
4、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0015.png">
|
||||||
|
|
||||||
5、
|
5、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0016.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6、
|
6、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0017.png">
|
||||||
|
|
||||||
7、
|
7、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0018.png">
|
||||||
|
|
||||||
8、
|
8、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0019.png">
|
||||||
|
|
||||||
9、
|
9、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0020.png">
|
||||||
|
|
||||||
10、
|
10、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0021.png">
|
||||||
|
|
||||||
11、图片写错了是#25和#26(获得System类)
|
11、图片写错了是#25和#26(获得System类)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0022.png">
|
||||||
|
|
||||||
12、
|
12、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0023.png">
|
||||||
|
|
||||||
13、
|
13、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0024.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0024.png">
|
||||||
|
|
||||||
15、执行加法运算后,将计算结果放在操作数栈顶
|
15、执行加法运算后,将计算结果放在操作数栈顶
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0025.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0025.png">
|
||||||
|
|
||||||
16、就是真正的打印
|
16、就是真正的打印
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0026.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0026.png">
|
||||||
|
|
||||||
17、
|
17、
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0027.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0027.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1021,7 +1021,7 @@ SourceFile: "MethodAreaDemo.java"
|
|||||||
|
|
||||||
方法区由永久代实现,使用 JVM 虚拟机内存(虚拟的内存)
|
方法区由永久代实现,使用 JVM 虚拟机内存(虚拟的内存)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0028.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0028.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1029,7 +1029,7 @@ SourceFile: "MethodAreaDemo.java"
|
|||||||
|
|
||||||
方法区由永久代实现,使用 JVM 虚拟机内存
|
方法区由永久代实现,使用 JVM 虚拟机内存
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0029.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0029.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1037,7 +1037,7 @@ SourceFile: "MethodAreaDemo.java"
|
|||||||
|
|
||||||
方法区由元空间实现,使用物理机本地内存
|
方法区由元空间实现,使用物理机本地内存
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0030.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0030.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1095,15 +1095,15 @@ public class StaticFieldTest {
|
|||||||
|
|
||||||
JDK6环境下
|
JDK6环境下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0031.png" alt="image-20201113224231761" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0031.png" alt="image-20201113224231761" />
|
||||||
|
|
||||||
JDK7环境下
|
JDK7环境下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0032.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0032.png">
|
||||||
|
|
||||||
JDK8环境
|
JDK8环境
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0033.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0033.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1150,7 +1150,7 @@ public class StaticObjTest {
|
|||||||
|
|
||||||
4、测试发现:三个对象的数据在内存中的地址都落在Eden区范围内,所以结论:**只要是对象实例必然会在Java堆中分配**。
|
4、测试发现:三个对象的数据在内存中的地址都落在Eden区范围内,所以结论:**只要是对象实例必然会在Java堆中分配**。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0034.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0034.png">
|
||||||
|
|
||||||
> 1、0x00007f32c7800000(Eden区的起始地址) ---- 0x00007f32c7b50000(Eden区的终止地址)
|
> 1、0x00007f32c7800000(Eden区的起始地址) ---- 0x00007f32c7b50000(Eden区的终止地址)
|
||||||
>
|
>
|
||||||
@@ -1162,7 +1162,7 @@ public class StaticObjTest {
|
|||||||
|
|
||||||
5、接着,找到了一个引用该staticObj对象的地方,是在一个java.lang.Class的实例里,并且给出了这个实例的地址,通过Inspector查看该对象实例,可以清楚看到这确实是一个java.lang.Class类型的对象实例,里面有一个名为staticobj的实例字段:
|
5、接着,找到了一个引用该staticObj对象的地方,是在一个java.lang.Class的实例里,并且给出了这个实例的地址,通过Inspector查看该对象实例,可以清楚看到这确实是一个java.lang.Class类型的对象实例,里面有一个名为staticobj的实例字段:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0035.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0035.png">
|
||||||
|
|
||||||
从《Java虚拟机规范》所定义的概念模型来看,所有Class相关的信息都应该存放在方法区之中,但方法区该如何实现,《Java虚拟机规范》并未做出规定,这就成了一件允许不同虚拟机自己灵活把握的事情。JDK7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对象存放在一起,**存储于Java堆之中**,从我们的实验中也明确验证了这一点
|
从《Java虚拟机规范》所定义的概念模型来看,所有Class相关的信息都应该存放在方法区之中,但方法区该如何实现,《Java虚拟机规范》并未做出规定,这就成了一件允许不同虚拟机自己灵活把握的事情。JDK7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对象存放在一起,**存储于Java堆之中**,从我们的实验中也明确验证了这一点
|
||||||
|
|
||||||
@@ -1217,7 +1217,7 @@ public class StaticObjTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0036.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0036.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1271,7 +1271,7 @@ public class BufferTest {
|
|||||||
|
|
||||||
直接占用了 1G 的本地内存
|
直接占用了 1G 的本地内存
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0037.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0037.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1281,13 +1281,13 @@ public class BufferTest {
|
|||||||
|
|
||||||
原来采用BIO的架构,在读写本地文件时,我们需要从用户态切换成内核态
|
原来采用BIO的架构,在读写本地文件时,我们需要从用户态切换成内核态
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0038.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0038.png">
|
||||||
|
|
||||||
**直接缓冲区(NIO)**
|
**直接缓冲区(NIO)**
|
||||||
|
|
||||||
NIO 直接操作物理磁盘,省去了中间过程
|
NIO 直接操作物理磁盘,省去了中间过程
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0039.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0039.png">
|
||||||
|
|
||||||
### 直接内存与 OOM
|
### 直接内存与 OOM
|
||||||
|
|
||||||
@@ -1351,7 +1351,7 @@ Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0040.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0040.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第7章-对象的实例化内存布局与访问定位。
|
description: JVM系列-第7章-对象的实例化内存布局与访问定位。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: debff71a
|
abbrlink: debff71a
|
||||||
date: 2020-11-14 19:38:42
|
date: 2020-11-14 19:38:42
|
||||||
---
|
---
|
||||||
@@ -36,7 +36,7 @@ date: 2020-11-14 19:38:42
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0001.png">
|
||||||
|
|
||||||
### 对象创建的方式
|
### 对象创建的方式
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ class Account{
|
|||||||
对象的内存布局
|
对象的内存布局
|
||||||
---------
|
---------
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ class Account{
|
|||||||
|
|
||||||
图解内存布局
|
图解内存布局
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ class Account{
|
|||||||
|
|
||||||
**JVM是如何通过栈帧中的对象引用访问到其内部的对象实例呢?**
|
**JVM是如何通过栈帧中的对象引用访问到其内部的对象实例呢?**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0004.png">
|
||||||
|
|
||||||
定位,通过栈上reference访问
|
定位,通过栈上reference访问
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ class Account{
|
|||||||
1. 缺点:在堆空间中开辟了一块空间作为句柄池,句柄池本身也会占用空间;通过两次指针访问才能访问到堆中的对象,效率低
|
1. 缺点:在堆空间中开辟了一块空间作为句柄池,句柄池本身也会占用空间;通过两次指针访问才能访问到堆中的对象,效率低
|
||||||
2. 优点:reference中存储稳定句柄地址,对象被移动(垃圾收集时移动对象很普遍)时只会改变句柄中实例数据指针即可,reference本身不需要被修改
|
2. 优点:reference中存储稳定句柄地址,对象被移动(垃圾收集时移动对象很普遍)时只会改变句柄中实例数据指针即可,reference本身不需要被修改
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -271,4 +271,4 @@ class Account{
|
|||||||
1. 优点:直接指针是局部变量表中的引用,直接指向堆中的实例,在对象实例中有类型指针,指向的是方法区中的对象类型数据
|
1. 优点:直接指针是局部变量表中的引用,直接指向堆中的实例,在对象实例中有类型指针,指向的是方法区中的对象类型数据
|
||||||
2. 缺点:对象被移动(垃圾收集时移动对象很普遍)时需要修改 reference 的值
|
2. 缺点:对象被移动(垃圾收集时移动对象很普遍)时需要修改 reference 的值
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0006.png">
|
||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第8章-执行引擎。
|
description: JVM系列-第8章-执行引擎。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: 408712f4
|
abbrlink: 408712f4
|
||||||
date: 2020-11-15 19:48:42
|
date: 2020-11-15 19:48:42
|
||||||
---
|
---
|
||||||
@@ -23,7 +23,7 @@ date: 2020-11-15 19:48:42
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0001.png">
|
||||||
|
|
||||||
### 执行引擎概述
|
### 执行引擎概述
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ date: 2020-11-15 19:48:42
|
|||||||
3. JVM的主要任务是负责**装载字节码到其内部**,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表,以及其他辅助信息。
|
3. JVM的主要任务是负责**装载字节码到其内部**,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表,以及其他辅助信息。
|
||||||
4. 那么,如果想要让一个Java程序运行起来,执行引擎(Execution Engine)的任务就是**将字节码指令解释/编译为对应平台上的本地机器指令才可以**。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
|
4. 那么,如果想要让一个Java程序运行起来,执行引擎(Execution Engine)的任务就是**将字节码指令解释/编译为对应平台上的本地机器指令才可以**。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0002.png">
|
||||||
|
|
||||||
1、前端编译:从Java程序员-字节码文件的这个过程叫前端编译
|
1、前端编译:从Java程序员-字节码文件的这个过程叫前端编译
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ date: 2020-11-15 19:48:42
|
|||||||
3. 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。
|
3. 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。
|
||||||
4. 从外观上来看,所有的Java虚拟机的执行引擎输入、处理、输出都是一致的:输入的是字节码二进制流,处理过程是字节码解析执行、即时编译的等效过程,输出的是执行过程。
|
4. 从外观上来看,所有的Java虚拟机的执行引擎输入、处理、输出都是一致的:输入的是字节码二进制流,处理过程是字节码解析执行、即时编译的等效过程,输出的是执行过程。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -71,18 +71,18 @@ Java代码编译和执行过程
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0004.png">
|
||||||
|
|
||||||
|
|
||||||
3. javac编译器(前端编译器)流程图如下所示:
|
3. javac编译器(前端编译器)流程图如下所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4. Java字节码的执行是由JVM执行引擎来完成,流程图如下所示
|
4. Java字节码的执行是由JVM执行引擎来完成,流程图如下所示
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Java代码编译和执行过程
|
|||||||
|
|
||||||
**用图总结一下**
|
**用图总结一下**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0007.png">
|
||||||
|
|
||||||
机器码 指令 汇编语言
|
机器码 指令 汇编语言
|
||||||
-------------
|
-------------
|
||||||
@@ -164,7 +164,7 @@ Java代码编译和执行过程
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0008.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ Java代码编译和执行过程
|
|||||||
2. 汇编过程:实际上指把汇编语言代码翻译成目标机器指令的过程。
|
2. 汇编过程:实际上指把汇编语言代码翻译成目标机器指令的过程。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ Java代码编译和执行过程
|
|||||||
3. 当一条字节码指令被解释执行完成后,接着再根据PC寄存器中记录的下一条需要被执行的字节码指令执行解释操作。
|
3. 当一条字节码指令被解释执行完成后,接着再根据PC寄存器中记录的下一条需要被执行的字节码指令执行解释操作。
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ Java代码编译和执行过程
|
|||||||
2. 在生产环境发布过程中,以分批的方式进行发布,根据机器数量划分成多个批次,每个批次的机器数至多占到整个集群的1/8。曾经有这样的故障案例:某程序员在发布平台进行分批发布,在输入发布总批数时,误填写成分为两批发布。如果是热机状态,在正常情况下一半的机器可以勉强承载流量,但由于刚启动的JVM均是解释执行,还没有进行热点代码统计和JIT动态编译,导致机器启动之后,当前1/2发布成功的服务器马上全部宕机,此故障说明了JIT的存在。—**阿里团队**
|
2. 在生产环境发布过程中,以分批的方式进行发布,根据机器数量划分成多个批次,每个批次的机器数至多占到整个集群的1/8。曾经有这样的故障案例:某程序员在发布平台进行分批发布,在输入发布总批数时,误填写成分为两批发布。如果是热机状态,在正常情况下一半的机器可以勉强承载流量,但由于刚启动的JVM均是解释执行,还没有进行热点代码统计和JIT动态编译,导致机器启动之后,当前1/2发布成功的服务器马上全部宕机,此故障说明了JIT的存在。—**阿里团队**
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0011.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ public class JITTest {
|
|||||||
|
|
||||||
通过 JVisualVM 查看 JIT 编译器执行的编译次数
|
通过 JVisualVM 查看 JIT 编译器执行的编译次数
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ public class JITTest {
|
|||||||
* 如果已超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。
|
* 如果已超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。
|
||||||
* 如果未超过阈值,则使用解释器对字节码文件解释执行
|
* 如果未超过阈值,则使用解释器对字节码文件解释执行
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ public class JITTest {
|
|||||||
|
|
||||||
它的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge)。显然,建立回边计数器统计的目的就是为了触发OSR编译。
|
它的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge)。显然,建立回边计数器统计的目的就是为了触发OSR编译。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0014.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -401,7 +401,7 @@ public class JITTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 1.内存与垃圾回收篇
|
- 1.内存与垃圾回收篇
|
||||||
keywords: JVM,虚拟机。
|
keywords: JVM,虚拟机。
|
||||||
description: JVM系列-第9章-StringTable(字符串常量池)。
|
description: JVM系列-第9章-StringTable(字符串常量池)。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/jvm.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png'
|
||||||
abbrlink: ee2ba71e
|
abbrlink: ee2ba71e
|
||||||
date: 2020-11-16 12:38:02
|
date: 2020-11-16 12:38:02
|
||||||
---
|
---
|
||||||
@@ -166,11 +166,11 @@ str 的内容并没有变:“test ok” 位于字符串常量池中的另一
|
|||||||
4. 在JDK7中,StringTable的长度默认值是60013,StringTablesize设置没有要求
|
4. 在JDK7中,StringTable的长度默认值是60013,StringTablesize设置没有要求
|
||||||
5. 在JDK8中,StringTable的长度默认值是60013,StringTable可以设置的最小值为1009
|
5. 在JDK8中,StringTable的长度默认值是60013,StringTable可以设置的最小值为1009
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -276,11 +276,11 @@ String 的内存分配
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -382,23 +382,23 @@ public class StringTest4 {
|
|||||||
|
|
||||||
1、程序启动时已经加载了 2293 个字符串常量
|
1、程序启动时已经加载了 2293 个字符串常量
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0005.png">
|
||||||
|
|
||||||
2、加载了一个换行符(println),所以多了一个
|
2、加载了一个换行符(println),所以多了一个
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0006.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0006.jpg">
|
||||||
|
|
||||||
3、加载了字符串常量 “1”~“9”
|
3、加载了字符串常量 “1”~“9”
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0007.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0007.jpg">
|
||||||
|
|
||||||
4、加载字符串常量 “10”
|
4、加载字符串常量 “10”
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0008.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0008.jpg">
|
||||||
|
|
||||||
5、之后的字符串"1" 到 "10"不会再次加载
|
5、之后的字符串"1" 到 "10"不会再次加载
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ class Memory {
|
|||||||
|
|
||||||
分析运行时内存(foo() 方法是实例方法,其实图中少了一个 this 局部变量)
|
分析运行时内存(foo() 方法是实例方法,其实图中少了一个 this 局部变量)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -493,7 +493,7 @@ class Memory {
|
|||||||
|
|
||||||
IDEA 反编译 class 文件后,来看这个问题
|
IDEA 反编译 class 文件后,来看这个问题
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0011.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -929,7 +929,7 @@ public class StringNewTest {
|
|||||||
5. `23 ldc #8 <b>` :在字符串常量池中放入 “b”(如果之前字符串常量池中没有 “b” 的话)
|
5. `23 ldc #8 <b>` :在字符串常量池中放入 “b”(如果之前字符串常量池中没有 “b” 的话)
|
||||||
6. `31 invokevirtual #9 <java/lang/StringBuilder.toString>` :调用 StringBuilder 的 toString() 方法,会生成一个 String 对象
|
6. `31 invokevirtual #9 <java/lang/StringBuilder.toString>` :调用 StringBuilder 的 toString() 方法,会生成一个 String 对象
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -988,13 +988,13 @@ JDK6 :正常眼光判断即可
|
|||||||
* new String() 即在堆中
|
* new String() 即在堆中
|
||||||
* str.intern() 则把字符串放入常量池中
|
* str.intern() 则把字符串放入常量池中
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
JDK7及后续版本,**注意大坑**
|
JDK7及后续版本,**注意大坑**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0014.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1053,11 +1053,11 @@ public class StringExer1 {
|
|||||||
|
|
||||||
**JDK6**
|
**JDK6**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**JDK7/8**
|
**JDK7/8**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0016.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1080,7 +1080,7 @@ public class StringExer1 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0017.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0017.png">
|
||||||
|
|
||||||
**练习3**
|
**练习3**
|
||||||
|
|
||||||
@@ -1169,11 +1169,11 @@ public class StringIntern2 {
|
|||||||
arr[i] = new String(String.valueOf(data[i % data.length]));
|
arr[i] = new String(String.valueOf(data[i % data.length]));
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0018.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0019.png">
|
||||||
|
|
||||||
2、使用 intern() 方法:由于数组中字符串的引用都指向字符串常量池中的字符串,所以程序需要维护的 String 对象更少,内存占用也更低
|
2、使用 intern() 方法:由于数组中字符串的引用都指向字符串常量池中的字符串,所以程序需要维护的 String 对象更少,内存占用也更低
|
||||||
|
|
||||||
@@ -1182,11 +1182,11 @@ arr[i] = new String(String.valueOf(data[i % data.length]));
|
|||||||
arr[i] = new String(String.valueOf(data[i % data.length])).intern();
|
arr[i] = new String(String.valueOf(data[i % data.length])).intern();
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0020.png" >
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0020.png" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0021.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1218,11 +1218,11 @@ public class StringGCTest {
|
|||||||
* Number of entries 和 Number of literals 明显没有 100000
|
* Number of entries 和 Number of literals 明显没有 100000
|
||||||
* 以上两点均说明 StringTable 区发生了垃圾回收
|
* 以上两点均说明 StringTable 区发生了垃圾回收
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 新特性
|
- 新特性
|
||||||
keywords: Java8,新特性,JDK8
|
keywords: Java8,新特性,JDK8
|
||||||
description: 详解JDK8出现的新特性。
|
description: 详解JDK8出现的新特性。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/java.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/java.png'
|
||||||
abbrlink: de3879ae
|
abbrlink: de3879ae
|
||||||
date: 2020-10-19 22:15:58
|
date: 2020-10-19 22:15:58
|
||||||
---
|
---
|
||||||
@@ -20,7 +20,7 @@ date: 2020-10-19 22:15:58
|
|||||||
|
|
||||||
> 本篇文章只讲解比较重要的
|
> 本篇文章只讲解比较重要的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -308,13 +308,13 @@ public class LambdaTest1 {
|
|||||||
|
|
||||||
**核心函数式接口**
|
**核心函数式接口**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**其它函数式接口**
|
**其它函数式接口**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -902,7 +902,7 @@ Stream到底是什么呢?
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1219,7 +1219,7 @@ public class StreamAPITest2 {
|
|||||||
|
|
||||||
## 常用API
|
## 常用API
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 重难点
|
- 重难点
|
||||||
keywords: Java基础,泛型
|
keywords: Java基础,泛型
|
||||||
description: 万字长文详解Java泛型。
|
description: 万字长文详解Java泛型。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/java.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/java.png'
|
||||||
abbrlink: adb2faf0
|
abbrlink: adb2faf0
|
||||||
date: 2020-10-19 22:21:58
|
date: 2020-10-19 22:21:58
|
||||||
---
|
---
|
||||||
@@ -1038,7 +1038,7 @@ class Dog extends Animal {
|
|||||||
|
|
||||||
`test1()`在编译时就会飘红
|
`test1()`在编译时就会飘红
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Generic/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Generic/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1290,7 +1290,7 @@ public class Test_difference {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Generic/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_Basis/Generic/0002.png">
|
||||||
|
|
||||||
### 区别3:?通配符可以使用超类限定而T不行
|
### 区别3:?通配符可以使用超类限定而T不行
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- HashMap
|
- HashMap
|
||||||
keywords: Java集合,HashMap。
|
keywords: Java集合,HashMap。
|
||||||
description: HashMap-JDK7源码讲解。
|
description: HashMap-JDK7源码讲解。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/java.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/java.png'
|
||||||
abbrlink: f1f58db2
|
abbrlink: f1f58db2
|
||||||
date: 2020-11-01 10:21:58
|
date: 2020-11-01 10:21:58
|
||||||
---
|
---
|
||||||
@@ -202,7 +202,7 @@ hadoop2
|
|||||||
|
|
||||||
大致是这样的一个结构
|
大致是这样的一个结构
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
|
||||||
|
|
||||||
- 每个链表就算哈希表的桶(bucket)
|
- 每个链表就算哈希表的桶(bucket)
|
||||||
- 链表的节点值就算一个键值对
|
- 链表的节点值就算一个键值对
|
||||||
@@ -398,7 +398,7 @@ static class Entry<K,V> implements Map.Entry<K,V> {
|
|||||||
|
|
||||||
`HashMap`中的数组元素 & 链表节点 采用 `Entry`类实现
|
`HashMap`中的数组元素 & 链表节点 采用 `Entry`类实现
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
|
||||||
|
|
||||||
1、一个正方形代表一个Entry对象,同时也代表一个键值对。
|
1、一个正方形代表一个Entry对象,同时也代表一个键值对。
|
||||||
|
|
||||||
@@ -769,15 +769,15 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
大概画了一下图:
|
大概画了一下图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0002.png"/>
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0004.png">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -871,7 +871,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
**hashmap初始状态**
|
**hashmap初始状态**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -897,7 +897,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
**两个线程调用完毕之后,hashmap目前是这样的。**
|
**两个线程调用完毕之后,hashmap目前是这样的。**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -918,9 +918,9 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
3、来看下此时内存里的状态
|
3、来看下此时内存里的状态
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0008.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0008.png"/>
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0009.png">
|
||||||
|
|
||||||
## 步骤4
|
## 步骤4
|
||||||
|
|
||||||
@@ -957,7 +957,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
2、线程2直接**扩容完毕**,那么完成后的状态是这样【假设e2和e3还是hash到同一个位置】
|
2、线程2直接**扩容完毕**,那么完成后的状态是这样【假设e2和e3还是hash到同一个位置】
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0010.png">
|
||||||
|
|
||||||
3、线程1还是原来的状态
|
3、线程1还是原来的状态
|
||||||
|
|
||||||
@@ -967,11 +967,11 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
目前两个线程里的新数组是这样的
|
目前两个线程里的新数组是这样的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0011.png">
|
||||||
|
|
||||||
为了方便后面观看,我画成这样。
|
为了方便后面观看,我画成这样。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0012.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1015,7 +1015,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
也就变成了下面这个样子。
|
也就变成了下面这个样子。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0013.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1061,7 +1061,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
执行完,变成这样。
|
执行完,变成这样。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0014.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1077,7 +1077,7 @@ void transfer(Entry[] newTable, boolean rehash) {
|
|||||||
|
|
||||||
3、执行pos_3: newTable[i] = e得到 newTable1[3] == e2
|
3、执行pos_3: newTable[i] = e得到 newTable1[3] == e2
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0015.png">
|
||||||
|
|
||||||
这样就形成了循环链表,再get()数据就会陷入死循环。
|
这样就形成了循环链表,再get()数据就会陷入死循环。
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- HashMap
|
- HashMap
|
||||||
keywords: Java集合,HashMap。
|
keywords: Java集合,HashMap。
|
||||||
description: HashMap-JDK8源码讲解及常见面试题。
|
description: HashMap-JDK8源码讲解及常见面试题。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/java.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/java.png'
|
||||||
abbrlink: cbc5672a
|
abbrlink: cbc5672a
|
||||||
date: 2020-11-01 10:22:05
|
date: 2020-11-01 10:22:05
|
||||||
---
|
---
|
||||||
@@ -27,7 +27,7 @@ date: 2020-11-01 10:22:05
|
|||||||
|
|
||||||
在JDK8中,优化了HashMap的数据结构,引入了红黑树。即HashMap的数据结构:数组+链表+红黑树。HashMap变成了这样。
|
在JDK8中,优化了HashMap的数据结构,引入了红黑树。即HashMap的数据结构:数组+链表+红黑树。HashMap变成了这样。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0001.png">
|
||||||
|
|
||||||
### 为什么要引入红黑树
|
### 为什么要引入红黑树
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ Process finished with exit code 0
|
|||||||
|
|
||||||
JDK8 hash的运算原理:高位参与低位运算,使得hash更加均匀。
|
JDK8 hash的运算原理:高位参与低位运算,使得hash更加均匀。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -569,7 +569,7 @@ JDK8 hash的运算原理:高位参与低位运算,使得hash更加均匀。
|
|||||||
|
|
||||||
JDK8扩容时,数据在数组下标的计算方式
|
JDK8扩容时,数据在数组下标的计算方式
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0003.png">
|
||||||
|
|
||||||
* `JDK8`根据此结论作出的新元素存储位置计算规则非常简单,提高了扩容效率。
|
* `JDK8`根据此结论作出的新元素存储位置计算规则非常简单,提高了扩容效率。
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 01.设计思想
|
- 01.设计思想
|
||||||
keywords: 设计模式,设计思想
|
keywords: 设计模式,设计思想
|
||||||
description: 设计模式第一部分-常用设计思想。
|
description: 设计模式第一部分-常用设计思想。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: c3dcce5d
|
abbrlink: c3dcce5d
|
||||||
date: 2021-06-07 17:21:58
|
date: 2021-06-07 17:21:58
|
||||||
---
|
---
|
||||||
@@ -198,7 +198,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
|
|||||||
1. 这种设计思路虽然可以解决问题,但不够优美。因为除了鸵鸟之外,不会飞的鸟还有很多,比如企鹅。对于这些不会飞的鸟来说,我们都需要重写 fly() 方法,抛出异常。这样的设计,一方面,徒增了编码的工作量;另一方面,也违背了我们之后要讲的最小知识原则(Least Knowledge Principle,也叫最少知识原则或者迪米特法则),暴露不该暴露的接口给外部,增加了类使用过程中被误用的概率。
|
1. 这种设计思路虽然可以解决问题,但不够优美。因为除了鸵鸟之外,不会飞的鸟还有很多,比如企鹅。对于这些不会飞的鸟来说,我们都需要重写 fly() 方法,抛出异常。这样的设计,一方面,徒增了编码的工作量;另一方面,也违背了我们之后要讲的最小知识原则(Least Knowledge Principle,也叫最少知识原则或者迪米特法则),暴露不该暴露的接口给外部,增加了类使用过程中被误用的概率。
|
||||||
2. 你可能又会说,那我们再通过 AbstractBird 类派生出两个更加细分的抽象类:会飞的鸟类 AbstractFlyableBird 和不会飞的鸟类 AbstractUnFlyableBird,让麻雀、乌鸦这些会飞的鸟都继承 AbstractFlyableBird,让鸵鸟、企鹅这些不会飞的鸟,都继承 AbstractUnFlyableBird 类,不就可以了吗?具体的继承关系如下图所示:
|
2. 你可能又会说,那我们再通过 AbstractBird 类派生出两个更加细分的抽象类:会飞的鸟类 AbstractFlyableBird 和不会飞的鸟类 AbstractUnFlyableBird,让麻雀、乌鸦这些会飞的鸟都继承 AbstractFlyableBird,让鸵鸟、企鹅这些不会飞的鸟,都继承 AbstractUnFlyableBird 类,不就可以了吗?具体的继承关系如下图所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0001.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
|
|||||||
|
|
||||||
2. 是否会飞?是否会叫?两个行为搭配起来会产生四种情况:会飞会叫、不会飞会叫、会飞不会叫、不会飞不会叫。如果我们继续沿用刚才的设计思路,那就需要再定义四个抽象类(AbstractFlyableTweetableBird、AbstractFlyableUnTweetableBird、AbstractUnFlyableTweetableBird、AbstractUnFlyableUnTweetableBird)。
|
2. 是否会飞?是否会叫?两个行为搭配起来会产生四种情况:会飞会叫、不会飞会叫、会飞不会叫、不会飞不会叫。如果我们继续沿用刚才的设计思路,那就需要再定义四个抽象类(AbstractFlyableTweetableBird、AbstractFlyableUnTweetableBird、AbstractUnFlyableTweetableBird、AbstractUnFlyableUnTweetableBird)。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -376,7 +376,7 @@ demofunction(client);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0003.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 02.经典设计原则
|
- 02.经典设计原则
|
||||||
keywords: 设计模式,经典设计原则
|
keywords: 设计模式,经典设计原则
|
||||||
description: 设计模式-经典设计原则,例如:单一职责原则,开闭原则,接口隔离原则等等。
|
description: 设计模式-经典设计原则,例如:单一职责原则,开闭原则,接口隔离原则等等。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: fc1c7619
|
abbrlink: fc1c7619
|
||||||
date: 2021-06-13 19:21:58
|
date: 2021-06-13 19:21:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 02.经典设计原则
|
- 02.经典设计原则
|
||||||
keywords: 设计模式,经典设计原则
|
keywords: 设计模式,经典设计原则
|
||||||
description: 设计模式-经典设计原则,例如:迪米特法则,依赖反转原则,KISS等等。
|
description: 设计模式-经典设计原则,例如:迪米特法则,依赖反转原则,KISS等等。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: 994a8ed3
|
abbrlink: 994a8ed3
|
||||||
date: 2021-06-20 19:21:58
|
date: 2021-06-20 19:21:58
|
||||||
---
|
---
|
||||||
@@ -724,7 +724,7 @@ public class UserRepo {
|
|||||||
|
|
||||||
前面也提到,“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。关于这一点,我画了一张对比图来解释。图中左边部分的代码结构是“高内聚、松耦合”;右边部分正好相反,是“低内聚、紧耦合”。
|
前面也提到,“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。关于这一点,我画了一张对比图来解释。图中左边部分的代码结构是“高内聚、松耦合”;右边部分正好相反,是“低内聚、紧耦合”。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_principles/0001.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/design_principles/0001.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 03.创建型
|
- 03.创建型
|
||||||
keywords: 设计模式,单例
|
keywords: 设计模式,单例
|
||||||
description: 详解了单例设计模式。
|
description: 详解了单例设计模式。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: b5a1ed4a
|
abbrlink: b5a1ed4a
|
||||||
date: 2021-06-26 21:51:58
|
date: 2021-06-26 21:51:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ categories:
|
|||||||
- 03.创建型
|
- 03.创建型
|
||||||
keywords: 设计模式,工厂,建造者,原型
|
keywords: 设计模式,工厂,建造者,原型
|
||||||
description: 详解常用的工厂模式和建造者模式,以及不常用的原型模式
|
description: 详解常用的工厂模式和建造者模式,以及不常用的原型模式
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: ba432704
|
abbrlink: ba432704
|
||||||
date: 2021-06-27 00:51:58
|
date: 2021-06-27 00:51:58
|
||||||
---
|
---
|
||||||
@@ -727,7 +727,7 @@ public class BeansFactory {
|
|||||||
1. 在平时的开发中,创建一个对象最常用的方式是,使用 new 关键字调用类的构造函数来完成。我的问题是,什么情况下这种方式就不适用了,就需要采用建造者模式来创建对象呢?你可以先思考一下,下面我通过一个例子来带你看一下。
|
1. 在平时的开发中,创建一个对象最常用的方式是,使用 new 关键字调用类的构造函数来完成。我的问题是,什么情况下这种方式就不适用了,就需要采用建造者模式来创建对象呢?你可以先思考一下,下面我通过一个例子来带你看一下。
|
||||||
2. 假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。现在,请你编写代码实现这个 ResourcePoolConfig 类。
|
2. 假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。现在,请你编写代码实现这个 ResourcePoolConfig 类。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0001.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0001.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1003,7 +1003,7 @@ r.setHeight(3); // r is valid
|
|||||||
2. 如果你熟悉的是 Java 语言,可以直接使用语言中提供的 HashMap 容器来实现。其中,HashMap 的 key 为搜索关键词,value 为关键词详细信息(比如搜索次数)。我们只需要将数据从数据库中读取出来,放入 HashMap 就可以了。
|
2. 如果你熟悉的是 Java 语言,可以直接使用语言中提供的 HashMap 容器来实现。其中,HashMap 的 key 为搜索关键词,value 为关键词详细信息(比如搜索次数)。我们只需要将数据从数据库中读取出来,放入 HashMap 就可以了。
|
||||||
3. 不过,我们还有另外一个系统 B,专门用来分析搜索日志,定期(比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为。
|
3. 不过,我们还有另外一个系统 B,专门用来分析搜索日志,定期(比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1150,15 +1150,15 @@ public class Demo {
|
|||||||
|
|
||||||
我们来看,在内存中,用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,大致结构如下所示。从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词,value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外的内存空间中。
|
我们来看,在内存中,用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,大致结构如下所示。从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词,value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外的内存空间中。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0003.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0003.png" />
|
||||||
|
|
||||||
浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据(SearchWord 对象)本身。相反,深拷贝不仅仅会复制索引,还会复制数据本身。浅拷贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据(SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。具体的对比如下图所示:
|
浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据(SearchWord 对象)本身。相反,深拷贝不仅仅会复制索引,还会复制数据本身。浅拷贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据(SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。具体的对比如下图所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0004.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0005.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0005.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ categories:
|
|||||||
- 04.结构型
|
- 04.结构型
|
||||||
keywords: 设计模式,代理模式,桥接模式,装饰器模式,适配器模式
|
keywords: 设计模式,代理模式,桥接模式,装饰器模式,适配器模式
|
||||||
description: 对代理模式,桥接模式,装饰器模式,适配器模式这4个模式进行了比较详细的讲述。其实学习设计模式主要是为了后序看源码
|
description: 对代理模式,桥接模式,装饰器模式,适配器模式这4个模式进行了比较详细的讲述。其实学习设计模式主要是为了后序看源码
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: 926a065c
|
abbrlink: 926a065c
|
||||||
date: 2021-07-04 00:51:58
|
date: 2021-07-04 00:51:58
|
||||||
---
|
---
|
||||||
@@ -608,7 +608,7 @@ IUserController userController = (IUserController) proxy.createProxy(new UserCon
|
|||||||
|
|
||||||
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
|
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0001.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -616,7 +616,7 @@ IUserController userController = (IUserController) proxy.createProxy(new UserCon
|
|||||||
|
|
||||||
传统方法对应的类图
|
传统方法对应的类图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0007.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0007.png"/>
|
||||||
|
|
||||||
1. 扩展性问题(**类爆炸**),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
|
1. 扩展性问题(**类爆炸**),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
|
||||||
2. 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
|
2. 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
|
||||||
@@ -933,7 +933,7 @@ public class DriverManager {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1112,7 +1112,7 @@ public class TrivialNotification extends Notification {
|
|||||||
|
|
||||||
### 方案一
|
### 方案一
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0003.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1127,7 +1127,7 @@ public class TrivialNotification extends Notification {
|
|||||||
|
|
||||||
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
|
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1142,7 +1142,7 @@ public class TrivialNotification extends Notification {
|
|||||||
|
|
||||||
### 装饰器模式代码
|
### 装饰器模式代码
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0005.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0005.png"/>
|
||||||
|
|
||||||
#### Drink【抽象类-主体Component】
|
#### Drink【抽象类-主体Component】
|
||||||
|
|
||||||
@@ -1390,7 +1390,7 @@ Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读
|
|||||||
|
|
||||||
针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:
|
针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0006.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0006.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ categories:
|
|||||||
- 04.结构型
|
- 04.结构型
|
||||||
keywords: 设计模式,门面模式,组合模式,享元模式
|
keywords: 设计模式,门面模式,组合模式,享元模式
|
||||||
description: 对代理模式,门面模式,组合模式,享元模式这3个设计模式进行了比较详细的讲述。
|
description: 对代理模式,门面模式,组合模式,享元模式这3个设计模式进行了比较详细的讲述。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: 5ea604e0
|
abbrlink: 5ea604e0
|
||||||
date: 2021-07-05 16:51:58
|
date: 2021-07-05 16:51:58
|
||||||
---
|
---
|
||||||
@@ -75,7 +75,7 @@ date: 2021-07-05 16:51:58
|
|||||||
|
|
||||||
### 传统方案
|
### 传统方案
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0001.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.02/0001.png" />
|
||||||
|
|
||||||
1. 在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程 混乱,没有清晰的过程 不利于在 ClientTest 中,去维护对子系统的操作
|
1. 在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程 混乱,没有清晰的过程 不利于在 ClientTest 中,去维护对子系统的操作
|
||||||
|
|
||||||
@@ -623,7 +623,7 @@ public class Demo {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.02/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 05.行为型
|
- 05.行为型
|
||||||
keywords: 观察者模式,模板模式
|
keywords: 观察者模式,模板模式
|
||||||
description: 观察者模式,模板模式。很常用的两个模式
|
description: 观察者模式,模板模式。很常用的两个模式
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: dd09051e
|
abbrlink: dd09051e
|
||||||
date: 2021-07-14 16:51:58
|
date: 2021-07-14 16:51:58
|
||||||
---
|
---
|
||||||
@@ -34,7 +34,7 @@ date: 2021-07-14 16:51:58
|
|||||||
|
|
||||||
### 方案一
|
### 方案一
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.01/0001.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -721,11 +721,11 @@ public DObserver{
|
|||||||
1. Guava EventBus 的功能我们已经讲清楚了,总体上来说,还是比较简单的。接下来,我们就重复造轮子,“山寨”一个 EventBus 出来。
|
1. Guava EventBus 的功能我们已经讲清楚了,总体上来说,还是比较简单的。接下来,我们就重复造轮子,“山寨”一个 EventBus 出来。
|
||||||
2. 我们重点来看,EventBus 中两个核心函数 register() 和 post() 的实现原理。弄懂了它们,基本上就弄懂了整个 EventBus 框架。下面两张图是这两个函数的实现原理图。
|
2. 我们重点来看,EventBus 中两个核心函数 register() 和 post() 的实现原理。弄懂了它们,基本上就弄懂了整个 EventBus 框架。下面两张图是这两个函数的实现原理图。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.01/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.01/0003.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 05.行为型
|
- 05.行为型
|
||||||
keywords: 策略模式,职责链模式
|
keywords: 策略模式,职责链模式
|
||||||
description: 不多说,看文章
|
description: 不多说,看文章
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: 2c3cc5fd
|
abbrlink: 2c3cc5fd
|
||||||
date: 2021-08-01 15:51:58
|
date: 2021-08-01 15:51:58
|
||||||
---
|
---
|
||||||
@@ -1171,7 +1171,7 @@ public class SensitiveWordFilter {
|
|||||||
|
|
||||||
Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、Jetty 等),都支持过滤器功能。为了帮助你理解,我画了一张示意图阐述它的工作原理,如下所示。
|
Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、Jetty 等),都支持过滤器功能。为了帮助你理解,我画了一张示意图阐述它的工作原理,如下所示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.02/0001.png"/>
|
||||||
|
|
||||||
在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的示例代码,如下所示。添加一个过滤器,我们只需要定义一个实现 javax.servlet.Filter 接口的过滤器类,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置,创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。
|
在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的示例代码,如下所示。添加一个过滤器,我们只需要定义一个实现 javax.servlet.Filter 接口的过滤器类,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置,创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。
|
||||||
|
|
||||||
@@ -1279,7 +1279,7 @@ ApplicationFilterChain 中的 doFilter() 函数的代码实现比较有技巧,
|
|||||||
1. 刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。
|
1. 刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。
|
||||||
2. 它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。我画了一张图来阐述一个请求的处理流程,具体如下所示。
|
2. 它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。我画了一张图来阐述一个请求的处理流程,具体如下所示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.02/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 05.行为型
|
- 05.行为型
|
||||||
keywords: 状态模式,迭代器模式
|
keywords: 状态模式,迭代器模式
|
||||||
description: 看文章
|
description: 看文章
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/design_patterns.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/design_patterns.jpg'
|
||||||
abbrlink: 877f4ef2
|
abbrlink: 877f4ef2
|
||||||
date: 2021-08-02 15:51:58
|
date: 2021-08-02 15:51:58
|
||||||
---
|
---
|
||||||
@@ -30,7 +30,7 @@ date: 2021-08-02 15:51:58
|
|||||||
4. 实际上,马里奥形态的转变就是一个状态机。其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动作”。比如,吃蘑菇这个事件,会触发状态的转移:从小马里奥转移到超级马里奥,以及触发动作的执行(增加 100 积分)。
|
4. 实际上,马里奥形态的转变就是一个状态机。其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动作”。比如,吃蘑菇这个事件,会触发状态的转移:从小马里奥转移到超级马里奥,以及触发动作的执行(增加 100 积分)。
|
||||||
5. 为了方便接下来的讲解,我对游戏背景做了简化,只保留了部分状态和事件。简化之后的状态转移如下图所示:
|
5. 为了方便接下来的讲解,我对游戏背景做了简化,只保留了部分状态和事件。简化之后的状态转移如下图所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0001.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ public class MarioStateMachine {
|
|||||||
1. 实际上,上面这种实现方法有点类似 hard code,对于复杂的状态机来说不适用,而状态机的第二种实现方式查表法,就更加合适了。接下来,我们就一块儿来看下,如何利用查表法来补全骨架代码。
|
1. 实际上,上面这种实现方法有点类似 hard code,对于复杂的状态机来说不适用,而状态机的第二种实现方式查表法,就更加合适了。接下来,我们就一块儿来看下,如何利用查表法来补全骨架代码。
|
||||||
2. 实际上,除了用状态转移图来表示之外,状态机还可以用二维表来表示,如下所示。在这个二维表中,第一维表示当前状态,第二维表示事件,值表示当前状态经过事件之后,转移到的新状态及其执行的动作。
|
2. 实际上,除了用状态转移图来表示之外,状态机还可以用二维表来表示,如下所示。在这个二维表中,第一维表示当前状态,第二维表示事件,值表示当前状态经过事件之后,转移到的新状态及其执行的动作。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0002.png"/>
|
||||||
|
|
||||||
3. 相对于分支逻辑的实现方式,查表法的代码实现更加清晰,可读性和可维护性更好。当修改状态机时,我们只需要修改 transitionTable 和 actionTable 两个二维数组即可。实际上,如果我们把这两个二维数组存储在配置文件中,当需要修改状态机时,我们甚至可以不修改任何代码,只需要修改配置文件就可以了。具体的代码如下所示:
|
3. 相对于分支逻辑的实现方式,查表法的代码实现更加清晰,可读性和可维护性更好。当修改状态机时,我们只需要修改 transitionTable 和 actionTable 两个二维数组即可。实际上,如果我们把这两个二维数组存储在配置文件中,当需要修改状态机时,我们甚至可以不修改任何代码,只需要修改配置文件就可以了。具体的代码如下所示:
|
||||||
|
|
||||||
@@ -511,7 +511,7 @@ public class MarioStateMachine {
|
|||||||
2. 在开篇中我们讲到,它用来遍历集合对象。这里说的“集合对象”也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如数组、链表、树、图、跳表。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
|
2. 在开篇中我们讲到,它用来遍历集合对象。这里说的“集合对象”也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如数组、链表、树、图、跳表。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
|
||||||
3. 迭代器是用来遍历容器的,所以,一个完整的迭代器模式一般会涉及**容器和容器迭代器**部分两部分内容。为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类。对于迭代器模式,我画了一张简单的类图,你可以看一看,先有个大致的印象。
|
3. 迭代器是用来遍历容器的,所以,一个完整的迭代器模式一般会涉及**容器和容器迭代器**部分两部分内容。为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类。对于迭代器模式,我画了一张简单的类图,你可以看一看,先有个大致的印象。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ public class Demo {
|
|||||||
2. 结合刚刚的例子,我们来总结一下迭代器的设计思路。总结下来就三句话:迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。
|
2. 结合刚刚的例子,我们来总结一下迭代器的设计思路。总结下来就三句话:迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。
|
||||||
3. 这里我画了一张类图,如下所示。实际上就是对上面那张类图的细化,你可以结合着一块看。
|
3. 这里我画了一张类图,如下所示。实际上就是对上面那张类图的细化,你可以结合着一块看。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0004.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -752,7 +752,7 @@ public class Demo {
|
|||||||
4. 为了保持数组存储数据的连续性,数组的删除操作会涉及元素的搬移。当执行到第 57 行代码的时候,我们从数组中将元素 a 删除掉,b、c、d 三个元素会依次往前搬移一位,这就会导致游标本来指向元素 b,现在变成了指向元素 c。原本在执行完第 56 行代码之后,我们还可以遍历到 b、c、d 三个元素,但在执行完第 57 行代码之后,我们只能遍历到 c、d 两个元素,b 遍历不到了。
|
4. 为了保持数组存储数据的连续性,数组的删除操作会涉及元素的搬移。当执行到第 57 行代码的时候,我们从数组中将元素 a 删除掉,b、c、d 三个元素会依次往前搬移一位,这就会导致游标本来指向元素 b,现在变成了指向元素 c。原本在执行完第 56 行代码之后,我们还可以遍历到 b、c、d 三个元素,但在执行完第 57 行代码之后,我们只能遍历到 c、d 两个元素,b 遍历不到了。
|
||||||
5. 对于上面的描述,我画了一张图,你可以对照着理解。
|
5. 对于上面的描述,我画了一张图,你可以对照着理解。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0005.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0005.png"/>
|
||||||
|
|
||||||
6. 不过,如果第 57 行代码删除的不是游标前面的元素(元素 a)以及游标所在位置的元素(元素 b),而是游标后面的元素(元素 c 和 d),这样就不会存在任何问题了,不会存在某个元素遍历不到的情况了。
|
6. 不过,如果第 57 行代码删除的不是游标前面的元素(元素 a)以及游标所在位置的元素(元素 b),而是游标后面的元素(元素 c 和 d),这样就不会存在任何问题了,不会存在某个元素遍历不到的情况了。
|
||||||
7. 所以,我们前面说,在遍历的过程中删除集合元素,结果是不可预期的,有时候没问题(删除元素 c 或 d),有时候就有问题(删除元素 a 或 b),这个要视情况而定(到底删除的是哪个位置的元素),就是这个意思。
|
7. 所以,我们前面说,在遍历的过程中删除集合元素,结果是不可预期的,有时候没问题(删除元素 c 或 d),有时候就有问题(删除元素 a 或 b),这个要视情况而定(到底删除的是哪个位置的元素),就是这个意思。
|
||||||
@@ -778,7 +778,7 @@ public class Demo {
|
|||||||
10. 跟删除情况类似,如果我们在游标的后面添加元素,就不会存在任何问题。所以,在遍历的同时添加集合元素也是一种不可预期行为。
|
10. 跟删除情况类似,如果我们在游标的后面添加元素,就不会存在任何问题。所以,在遍历的同时添加集合元素也是一种不可预期行为。
|
||||||
11. 同样,对于上面的添加元素的情况,我们也画了一张图,如下所示,你可以对照着理解。
|
11. 同样,对于上面的添加元素的情况,我们也画了一张图,如下所示,你可以对照着理解。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0006.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0006.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: 前两节合成一节
|
description: 前两节合成一节
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: d3c530c4
|
abbrlink: d3c530c4
|
||||||
date: 2021-09-11 15:21:58
|
date: 2021-09-11 15:21:58
|
||||||
---
|
---
|
||||||
@@ -97,7 +97,7 @@ Dubbo网关参考:[https://github.com/apache/dubbo-proxy](https://github.com/a
|
|||||||
|
|
||||||
### 基本原理
|
### 基本原理
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.1/rpc/dubbo/v1/01_di_yi_jie/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_yi_jie/0001.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -914,11 +914,11 @@ dubbo.config-center.address=
|
|||||||
|
|
||||||
2. 管理台的**配置管理**作用就是可以实时更改dubbo相关的配置,在这里面写了和在appliaction.properties里面写是一样的效果,这个还不用重启服务。如果appliaction.properties里和管理台写了相同的配置,以管理台的为主。
|
2. 管理台的**配置管理**作用就是可以实时更改dubbo相关的配置,在这里面写了和在appliaction.properties里面写是一样的效果,这个还不用重启服务。如果appliaction.properties里和管理台写了相同的配置,以管理台的为主。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0001.png"/>
|
||||||
|
|
||||||
3. **动态配置**这里,也可以很方便的替代服务提供者@service注解上标注的那些配置。管理台是实时生效的,如果改代码里的@service还需要重启服务。
|
3. **动态配置**这里,也可以很方便的替代服务提供者@service注解上标注的那些配置。管理台是实时生效的,如果改代码里的@service还需要重启服务。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0002.png"/>
|
||||||
|
|
||||||
很多配置都可以在管理台上配。管理台上写的配置会持久化在**你配置的配置中心**里。只有注册中心里的服务提供者信息不持久化,如果注册中心是zookeeper,那么服务提供者在zk上就是临时节点。
|
很多配置都可以在管理台上配。管理台上写的配置会持久化在**你配置的配置中心**里。只有注册中心里的服务提供者信息不持久化,如果注册中心是zookeeper,那么服务提供者在zk上就是临时节点。
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: Dubbo里面SPI是基础,大量用到了SPI
|
description: Dubbo里面SPI是基础,大量用到了SPI
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: dbcfef47
|
abbrlink: dbcfef47
|
||||||
date: 2021-09-12 15:21:58
|
date: 2021-09-12 15:21:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: Spring与Dubbo整合原理与源码分析
|
description: Spring与Dubbo整合原理与源码分析
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: 796f395d
|
abbrlink: 796f395d
|
||||||
date: 2021-10-06 13:21:58
|
date: 2021-10-06 13:21:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: Dubbo服务导出源码解析
|
description: Dubbo服务导出源码解析
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: '48141866'
|
abbrlink: '48141866'
|
||||||
date: 2021-10-06 14:11:58
|
date: 2021-10-06 14:11:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: 服务引入源码解析
|
description: 服务引入源码解析
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: bda15919
|
abbrlink: bda15919
|
||||||
date: 2021-11-08 14:11:58
|
date: 2021-11-08 14:11:58
|
||||||
---
|
---
|
||||||
@@ -243,7 +243,7 @@ public class Application {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/Dubbo监听架构图.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/Dubbo监听架构图.png"/>
|
||||||
|
|
||||||
### 源码分析-解析@Reference注解上的配置
|
### 源码分析-解析@Reference注解上的配置
|
||||||
|
|
||||||
@@ -438,19 +438,19 @@ public class Application {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210915224055718.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210915224055718.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dubbo官方给的Demo没有配置URL,所以这里就是NULL
|
Dubbo官方给的Demo没有配置URL,所以这里就是NULL
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920151657492.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920151657492.png"/>
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920151857950.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920151857950.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152003415.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152003415.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -622,7 +622,7 @@ Dubbo官方给的Demo没有配置URL,所以这里就是NULL
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152813300.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152813300.png"/>
|
||||||
|
|
||||||
#### RegistryDirectory
|
#### RegistryDirectory
|
||||||
|
|
||||||
@@ -641,7 +641,7 @@ Dubbo官方给的Demo没有配置URL,所以这里就是NULL
|
|||||||
3. 看下面的截图,registry属性是zookeeper的URL,所以应该是要调用ZookeeperRegistry的subscribe()方法,但是ZookeeperRegistry没有这个方法,所以我们就要找它的父类了,也就是FailbackRegistry,
|
3. 看下面的截图,registry属性是zookeeper的URL,所以应该是要调用ZookeeperRegistry的subscribe()方法,但是ZookeeperRegistry没有这个方法,所以我们就要找它的父类了,也就是FailbackRegistry,
|
||||||
4. 然后再调用doSubscribe(),ZookeeperRegistry重写了此方法,很明显这是个模板模式。
|
4. 然后再调用doSubscribe(),ZookeeperRegistry重写了此方法,很明显这是个模板模式。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154023711.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154023711.png" />
|
||||||
|
|
||||||
#### FailbackRegistry
|
#### FailbackRegistry
|
||||||
|
|
||||||
@@ -843,7 +843,7 @@ Dubbo官方给的Demo没有配置URL,所以这里就是NULL
|
|||||||
|
|
||||||
最终走到了这一步
|
最终走到了这一步
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154648609.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154648609.png" />
|
||||||
|
|
||||||
#### RegistryDirectory
|
#### RegistryDirectory
|
||||||
|
|
||||||
@@ -1097,7 +1097,7 @@ consumer://192.168.0.100/org.apache.dubbo.demo.DemoService?application=dubbo-dem
|
|||||||
|
|
||||||
到此,路由链构造完毕。
|
到此,路由链构造完毕。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210919190129223.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210919190129223.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- Dubbo源码系列v1
|
- Dubbo源码系列v1
|
||||||
keywords: Dubbo,rpc
|
keywords: Dubbo,rpc
|
||||||
description: 服务调用源码解析
|
description: 服务调用源码解析
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/dubbo.png'
|
||||||
abbrlink: 84653c9d
|
abbrlink: 84653c9d
|
||||||
date: 2021-11-09 14:11:58
|
date: 2021-11-09 14:11:58
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: 万字系列长文讲解Java并发-第一阶段-多线程基础知识。
|
description: 万字系列长文讲解Java并发-第一阶段-多线程基础知识。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: efc79183
|
abbrlink: efc79183
|
||||||
date: 2020-10-05 22:40:58
|
date: 2020-10-05 22:40:58
|
||||||
---
|
---
|
||||||
@@ -31,13 +31,13 @@ date: 2020-10-05 22:40:58
|
|||||||
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
|
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
|
||||||
说明:线程作为CPU调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。
|
说明:线程作为CPU调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
补充:
|
补充:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0002.png">
|
||||||
|
|
||||||
进程可以细化为多个线程。
|
进程可以细化为多个线程。
|
||||||
每个线程,拥有自己独立的:栈、程序计数器
|
每个线程,拥有自己独立的:栈、程序计数器
|
||||||
@@ -281,7 +281,7 @@ private void init(ThreadGroup g, Runnable target, String name,
|
|||||||
1、多线程的设计之中,使用了代理设计模式的结构,用户自定义的线程主体只是负责项目核心功能的实现,而所有的辅助实现全部交由Thread类来处理。
|
1、多线程的设计之中,使用了代理设计模式的结构,用户自定义的线程主体只是负责项目核心功能的实现,而所有的辅助实现全部交由Thread类来处理。
|
||||||
2、在进行Thread启动多线程的时候调用的是start()方法,而后找到的是run()方法,但通过Thread类的构造方法传递了一个Runnable接口对象的时候,那么该接口对象将被Thread类中的target属性所保存,在start()方法执行的时候会调用Thread类中的run()方法。而这个run()方法去调用实现了Runnable接口的那个类所重写过run()方法,进而执行相应的逻辑。多线程开发的本质实质上是在于多个线程可以进行同一资源的抢占,那么Thread主要描述的是线程,而资源的描述是通过Runnable完成的。如下图所示:
|
2、在进行Thread启动多线程的时候调用的是start()方法,而后找到的是run()方法,但通过Thread类的构造方法传递了一个Runnable接口对象的时候,那么该接口对象将被Thread类中的target属性所保存,在start()方法执行的时候会调用Thread类中的run()方法。而这个run()方法去调用实现了Runnable接口的那个类所重写过run()方法,进而执行相应的逻辑。多线程开发的本质实质上是在于多个线程可以进行同一资源的抢占,那么Thread主要描述的是线程,而资源的描述是通过Runnable完成的。如下图所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -644,7 +644,7 @@ public void run() {
|
|||||||
|
|
||||||
1、如果直接调用run()方法,相当于就是简单的调用一个普通方法。
|
1、如果直接调用run()方法,相当于就是简单的调用一个普通方法。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0004.png">
|
||||||
|
|
||||||
2、run()的调用是在start0()这个Native C++方法里调用的
|
2、run()的调用是在start0()这个Native C++方法里调用的
|
||||||
|
|
||||||
@@ -654,11 +654,11 @@ public void run() {
|
|||||||
|
|
||||||
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态,这几个状态在Java源码中用枚举来表示。
|
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态,这几个状态在Java源码中用枚举来表示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0005.png">
|
||||||
|
|
||||||
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示。
|
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0006.png">
|
||||||
|
|
||||||
> 图中 wait到 runnable状态的转换中,`join`实际上是`Thread`类的方法,但这里写成了`Object`。
|
> 图中 wait到 runnable状态的转换中,`join`实际上是`Thread`类的方法,但这里写成了`Object`。
|
||||||
|
|
||||||
@@ -699,7 +699,7 @@ public static void main(String[] args) {
|
|||||||
|
|
||||||
2、当JVM启动后,实际有多个线程,但是至少有一个非守护线程(比如main线程)。
|
2、当JVM启动后,实际有多个线程,但是至少有一个非守护线程(比如main线程)。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0007.png">
|
||||||
|
|
||||||
- Finalizer:GC守护线程
|
- Finalizer:GC守护线程
|
||||||
|
|
||||||
@@ -1574,7 +1574,7 @@ volatile自己虽然不能保证原子性,但是和CAS结合起来就可以保
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1782,7 +1782,7 @@ public Unsafe getUnsafe() throws IllegalAccessException {
|
|||||||
|
|
||||||
Unsafe的功能如下图:
|
Unsafe的功能如下图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0008.png">
|
||||||
|
|
||||||
## CAS相关
|
## CAS相关
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: 万字系列长文讲解-Java并发体系-第三阶段-JUC并发包。JUC在高并发编程中使用频率非常高,这里会详细介绍其用法。
|
description: 万字系列长文讲解-Java并发体系-第三阶段-JUC并发包。JUC在高并发编程中使用频率非常高,这里会详细介绍其用法。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: 5be45d9e
|
abbrlink: 5be45d9e
|
||||||
date: 2020-10-09 22:13:58
|
date: 2020-10-09 22:13:58
|
||||||
---
|
---
|
||||||
@@ -1869,7 +1869,7 @@ public class ForkJoinRecursiveAction {
|
|||||||
|
|
||||||
ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:`RecursiveAction`和`RecursiveTask`。然后通过fork()方法去分配任务执行任务,通过join()方法汇总任务结果。
|
ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:`RecursiveAction`和`RecursiveTask`。然后通过fork()方法去分配任务执行任务,通过join()方法汇总任务结果。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0001.png">
|
||||||
|
|
||||||
## 小总结
|
## 小总结
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: 万字系列长文讲解-Java并发体系-第三阶段-JUC并发包。JUC在高并发编程中使用频率非常高,这里会详细介绍其用法。
|
description: 万字系列长文讲解-Java并发体系-第三阶段-JUC并发包。JUC在高并发编程中使用频率非常高,这里会详细介绍其用法。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: 70c90e5d
|
abbrlink: 70c90e5d
|
||||||
date: 2020-10-10 22:13:58
|
date: 2020-10-10 22:13:58
|
||||||
---
|
---
|
||||||
@@ -563,7 +563,7 @@ public int getUnarrivedParties()
|
|||||||
|
|
||||||
根据上面的代码,我们可以画出下面这个很简单的图:
|
根据上面的代码,我们可以画出下面这个很简单的图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0002.png">
|
||||||
|
|
||||||
这棵树上有 7 个 phaser 实例,每个 phaser 实例在构造的时候,都指定了 parties 为 5,但是,对于每个拥有子节点的节点来说,每个子节点都是它的一个 party,我们可以通过 phaser.getRegisteredParties() 得到每个节点的 parties 数量:
|
这棵树上有 7 个 phaser 实例,每个 phaser 实例在构造的时候,都指定了 parties 为 5,但是,对于每个拥有子节点的节点来说,每个子节点都是它的一个 party,我们可以通过 phaser.getRegisteredParties() 得到每个节点的 parties 数量:
|
||||||
|
|
||||||
@@ -952,7 +952,7 @@ public class ThreadPoolDemo {
|
|||||||
|
|
||||||
## 线程池的底层工作流程
|
## 线程池的底层工作流程
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0003.png">
|
||||||
|
|
||||||
1、创建线程池后,等待请求任务
|
1、创建线程池后,等待请求任务
|
||||||
|
|
||||||
@@ -1646,7 +1646,7 @@ public class ExecutorCompletionService<V> implements CompletionService<V> {
|
|||||||
|
|
||||||
**执行流程:**
|
**执行流程:**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: 230c5bb3
|
abbrlink: 230c5bb3
|
||||||
date: 2020-10-06 22:09:58
|
date: 2020-10-06 22:09:58
|
||||||
---
|
---
|
||||||
@@ -291,7 +291,7 @@ d = e - f ;
|
|||||||
|
|
||||||
由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
|
由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ int c = a + b;
|
|||||||
|
|
||||||
冯诺依曼,提出计算机由五大组成部分,输入设备,输出设备存储器,控制器,运算器。
|
冯诺依曼,提出计算机由五大组成部分,输入设备,输出设备存储器,控制器,运算器。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0002.png">
|
||||||
|
|
||||||
输入设备:鼠标,键盘等等
|
输入设备:鼠标,键盘等等
|
||||||
|
|
||||||
@@ -367,7 +367,7 @@ int c = a + b;
|
|||||||
|
|
||||||
CPU的运算速度和内存的访问速度相差比较大。这就导致CPU每次操作内存都要耗费很多等待时间。内 存的读写速度成为了计算机运行的瓶颈。于是就有了在CPU和主内存之间增加缓存的设计。靠近CPU 的缓存称为L1,然后依次是 L2,L3和主内存,CPU缓存模型如图下图所示。
|
CPU的运算速度和内存的访问速度相差比较大。这就导致CPU每次操作内存都要耗费很多等待时间。内 存的读写速度成为了计算机运行的瓶颈。于是就有了在CPU和主内存之间增加缓存的设计。靠近CPU 的缓存称为L1,然后依次是 L2,L3和主内存,CPU缓存模型如图下图所示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
|
||||||
|
|
||||||
CPU Cache分成了三个级别: L1, L2, L3。级别越小越接近CPU,速度也更快,同时也代表着容量越小。速度越快的价格越贵。
|
CPU Cache分成了三个级别: L1, L2, L3。级别越小越接近CPU,速度也更快,同时也代表着容量越小。速度越快的价格越贵。
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@ CPU Cache分成了三个级别: L1, L2, L3。级别越小越接近CPU,速
|
|||||||
|
|
||||||
3、L3 Cache是三级缓存中大的一级,例如12MB,同时也是缓存中慢的一级,在同一个CPU插槽 之间的核共享一个L3 Cache。
|
3、L3 Cache是三级缓存中大的一级,例如12MB,同时也是缓存中慢的一级,在同一个CPU插槽 之间的核共享一个L3 Cache。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
|
||||||
|
|
||||||
上面的图中有一个Latency指标。比如Memory这个指标为59.4ns,表示CPU在操作内存的时候有59.4ns的延迟,一级缓存最快只有1.2ns。
|
上面的图中有一个Latency指标。比如Memory这个指标为59.4ns,表示CPU在操作内存的时候有59.4ns的延迟,一级缓存最快只有1.2ns。
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ Cache的出现是为了解决CPU直接访问内存效率低下问题的。
|
|||||||
|
|
||||||
每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有的操 作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接 访问对方工作内存中的变量。
|
每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有的操 作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接 访问对方工作内存中的变量。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
|
||||||
|
|
||||||
Java的线程不能直接在主内存中操作共享变量。而是首先将主内存中的共享变量赋值到自己的工作内存中,再进行操作,操作完成之后,刷回主内存。
|
Java的线程不能直接在主内存中操作共享变量。而是首先将主内存中的共享变量赋值到自己的工作内存中,再进行操作,操作完成之后,刷回主内存。
|
||||||
|
|
||||||
@@ -424,7 +424,7 @@ Java内存模型是一套在多线程读写共享数据时,对共享数据的
|
|||||||
|
|
||||||
JMM内存模型与CPU硬件内存架构的关系:
|
JMM内存模型与CPU硬件内存架构的关系:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
|
||||||
|
|
||||||
工作内存:可能对应CPU寄存器,也可能对应CPU缓存,也可能对应内存。
|
工作内存:可能对应CPU寄存器,也可能对应CPU缓存,也可能对应内存。
|
||||||
|
|
||||||
@@ -434,9 +434,9 @@ JMM内存模型与CPU硬件内存架构的关系:
|
|||||||
|
|
||||||
## 再谈可见性
|
## 再谈可见性
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0007.png">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
|
||||||
|
|
||||||
1、图中所示是 个双核 CPU 系统架构 ,每个核有自己的控制器和运算器,其中控制器包含一组寄存器和操作控制器,运算器执行算术逻辅运算。每个核都有自己的1级缓存,在有些架构里面还有1个所有 CPU 共享的2级缓存。 那么 Java 内存模型里面的工作内存,就对应这里的 Ll 或者 L2 存或者 CPU 寄存器。
|
1、图中所示是 个双核 CPU 系统架构 ,每个核有自己的控制器和运算器,其中控制器包含一组寄存器和操作控制器,运算器执行算术逻辅运算。每个核都有自己的1级缓存,在有些架构里面还有1个所有 CPU 共享的2级缓存。 那么 Java 内存模型里面的工作内存,就对应这里的 Ll 或者 L2 存或者 CPU 寄存器。
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ JMM内存模型与CPU硬件内存架构的关系:
|
|||||||
|
|
||||||
为了保证数据交互时数据的正确性,Java内存模型中定义了8种操作来完成这个交互过程,这8种操作本身都是原子性的。虚拟机实现时必须保证下面 提及的每一种操作都是原子的、不可再分的。
|
为了保证数据交互时数据的正确性,Java内存模型中定义了8种操作来完成这个交互过程,这8种操作本身都是原子性的。虚拟机实现时必须保证下面 提及的每一种操作都是原子的、不可再分的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
|
||||||
|
|
||||||
> (1)lock:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
|
> (1)lock:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
|
||||||
>
|
>
|
||||||
@@ -478,7 +478,7 @@ JMM内存模型与CPU硬件内存架构的关系:
|
|||||||
|
|
||||||
如果没有synchronized,那就是下面这样的
|
如果没有synchronized,那就是下面这样的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -589,7 +589,7 @@ volatile不保证原子性,只保证可见性和禁止指令重排
|
|||||||
|
|
||||||
## CPU术语介绍
|
## CPU术语介绍
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -717,7 +717,7 @@ public class VolatileExample {
|
|||||||
|
|
||||||
**1、下面是保守策略下,volatile写插入内存屏障后生成的指令序列示意图**
|
**1、下面是保守策略下,volatile写插入内存屏障后生成的指令序列示意图**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0012.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0012.png">
|
||||||
|
|
||||||
> 图中的StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作已经对任 意处理器可见了。这是因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。这里比较有意思的是,volatile写后面的StoreLoad屏障。此屏障的作用是避免volatile写与 后面可能有的volatile读/写操作重排序。因为编译器常常无法准确判断在一个volatile写的后面 是否需要插入一个StoreLoad屏障(比如,一个volatile写之后方法立即return)。为了保证能正确 实现volatile的内存语义,JMM在采取了保守策略:在每个volatile写的后面,或者在每个volatile 读的前面插入一个StoreLoad屏障。从整体执行效率的角度考虑,JMM最终选择了在每个volatile写的后面插入一个StoreLoad屏障。因为volatile写-读内存语义的常见使用模式是:一个 写线程写volatile变量,多个读线程读同一个volatile变量。当读线程的数量大大超过写线程时, 选择在volatile写之后插入StoreLoad屏障将带来可观的执行效率的提升。从这里可以看到JMM在实现上的一个特点:首先确保正确性,然后再去追求执行效率
|
> 图中的StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作已经对任 意处理器可见了。这是因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。这里比较有意思的是,volatile写后面的StoreLoad屏障。此屏障的作用是避免volatile写与 后面可能有的volatile读/写操作重排序。因为编译器常常无法准确判断在一个volatile写的后面 是否需要插入一个StoreLoad屏障(比如,一个volatile写之后方法立即return)。为了保证能正确 实现volatile的内存语义,JMM在采取了保守策略:在每个volatile写的后面,或者在每个volatile 读的前面插入一个StoreLoad屏障。从整体执行效率的角度考虑,JMM最终选择了在每个volatile写的后面插入一个StoreLoad屏障。因为volatile写-读内存语义的常见使用模式是:一个 写线程写volatile变量,多个读线程读同一个volatile变量。当读线程的数量大大超过写线程时, 选择在volatile写之后插入StoreLoad屏障将带来可观的执行效率的提升。从这里可以看到JMM在实现上的一个特点:首先确保正确性,然后再去追求执行效率
|
||||||
|
|
||||||
@@ -725,7 +725,7 @@ public class VolatileExample {
|
|||||||
|
|
||||||
**2、下面是在保守策略下,volatile读插入内存屏障后生成的指令序列示意图**
|
**2、下面是在保守策略下,volatile读插入内存屏障后生成的指令序列示意图**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
|
||||||
|
|
||||||
> 图中的LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。 LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。 上述volatile写和volatile读的内存屏障插入策略非常保守。在实际执行时,只要不改变volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。
|
> 图中的LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。 LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。 上述volatile写和volatile读的内存屏障插入策略非常保守。在实际执行时,只要不改变volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。
|
||||||
|
|
||||||
@@ -752,7 +752,7 @@ class VolatileBarrierExample {
|
|||||||
|
|
||||||
针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化
|
针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
|
||||||
|
|
||||||
注意,最后的StoreLoad屏障不能省略。因为第二个volatile写之后,方法立即return。此时编译器可能无法准确断定后面是否会有volatile读或写,为了安全起见,编译器通常会在这里插入一个StoreLoad屏障。
|
注意,最后的StoreLoad屏障不能省略。因为第二个volatile写之后,方法立即return。此时编译器可能无法准确断定后面是否会有volatile读或写,为了安全起见,编译器通常会在这里插入一个StoreLoad屏障。
|
||||||
|
|
||||||
@@ -766,7 +766,7 @@ class VolatileBarrierExample {
|
|||||||
|
|
||||||
X86处理器仅会对写-读操作做重排序。X86不会对读-读、读-写和写-写操作 做重排序,因此在X86处理器中会省略掉这3种操作类型对应的内存屏障。在X86中,JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。这意味着在X86处理器中,volatile写的开销比volatile读的开销会大很多(因为执行StoreLoad屏障开销会比较大)。
|
X86处理器仅会对写-读操作做重排序。X86不会对读-读、读-写和写-写操作 做重排序,因此在X86处理器中会省略掉这3种操作类型对应的内存屏障。在X86中,JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。这意味着在X86处理器中,volatile写的开销比volatile读的开销会大很多(因为执行StoreLoad屏障开销会比较大)。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: '8210870'
|
abbrlink: '8210870'
|
||||||
date: 2020-10-07 22:10:58
|
date: 2020-10-07 22:10:58
|
||||||
---
|
---
|
||||||
@@ -18,7 +18,7 @@ date: 2020-10-07 22:10:58
|
|||||||
|
|
||||||
# 可见性设计的硬件
|
# 可见性设计的硬件
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0016.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0016.png">
|
||||||
|
|
||||||
从硬件的级别来考虑一下可见性的问题
|
从硬件的级别来考虑一下可见性的问题
|
||||||
|
|
||||||
@@ -305,13 +305,13 @@ MESI协议规定了一组消息,就说各个处理器在操作内存数据的
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0017.jpg">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0017.jpg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## MESI-优化
|
## MESI-优化
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0018.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0018.png">
|
||||||
|
|
||||||
MESI协议如果每次写数据的时候都要发送invalidate消息等待所有处理器返回ack,然后获取独占锁后才能写数据,那可能就会导致性能很差了,因为这个对共享变量的写操作,实际上在硬件级别变成串行的了。所以为了解决这个问题,硬件层面引入了写缓冲器和无效队列
|
MESI协议如果每次写数据的时候都要发送invalidate消息等待所有处理器返回ack,然后获取独占锁后才能写数据,那可能就会导致性能很差了,因为这个对共享变量的写操作,实际上在硬件级别变成串行的了。所以为了解决这个问题,硬件层面引入了写缓冲器和无效队列
|
||||||
|
|
||||||
@@ -449,7 +449,7 @@ int b = c; //load
|
|||||||
|
|
||||||
## 相关术语
|
## 相关术语
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0019.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0019.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ int b = c; //load
|
|||||||
|
|
||||||
**第一个机制是通过总线锁保证原子性。**如果多个处理器同时对共享变量进行读改写操作 (i++就是经典的读改写操作),那么共享变量就会被多个处理器同时进行操作,这样读改写操 作就不是原子的,操作完之后共享变量的值会和期望的不一致。举个例子,如果i=1,我们进行 两次i++操作,我们期望的结果是3,但是有可能结果是2,如图所示。
|
**第一个机制是通过总线锁保证原子性。**如果多个处理器同时对共享变量进行读改写操作 (i++就是经典的读改写操作),那么共享变量就会被多个处理器同时进行操作,这样读改写操 作就不是原子的,操作完之后共享变量的值会和期望的不一致。举个例子,如果i=1,我们进行 两次i++操作,我们期望的结果是3,但是有可能结果是2,如图所示。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0020.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0020.png">
|
||||||
|
|
||||||
原因可能是多个处理器同时从各自的缓存中读取变量i,分别进行加1操作,然后分别写入 系统内存中。那么,想要保证读改写共享变量的操作是原子的,就必须保证CPU1读改写共享 变量的时候,CPU2不能操作缓存了该共享变量内存地址的缓存。
|
原因可能是多个处理器同时从各自的缓存中读取变量i,分别进行加1操作,然后分别写入 系统内存中。那么,想要保证读改写共享变量的操作是原子的,就必须保证CPU1读改写共享 变量的时候,CPU2不能操作缓存了该共享变量内存地址的缓存。
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,原理,源码
|
keywords: Java并发,原理,源码
|
||||||
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
description: '万字系列长文讲解-Java并发体系-第二阶段,从C++和硬件方面讲解。'
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: 113a3931
|
abbrlink: 113a3931
|
||||||
date: 2020-10-08 22:10:58
|
date: 2020-10-08 22:10:58
|
||||||
---
|
---
|
||||||
@@ -357,17 +357,17 @@ monitorexit释放锁。 monitorexit插入在方法结束处和异常处,JVM保
|
|||||||
|
|
||||||
术语参考: http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html 在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。如下图所示:
|
术语参考: http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html 在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。如下图所示:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0021.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0021.png">
|
||||||
|
|
||||||
## 对象头
|
## 对象头
|
||||||
|
|
||||||
当一个线程尝试访问synchronized修饰的代码块时,它首先要获得锁,那么这个锁到底存在哪里呢?是 存在锁对象的对象头中的。 HotSpot采用instanceOopDesc和arrayOopDesc来描述对象头,arrayOopDesc对象用来描述数组类型。instanceOopDesc的定义的在Hotspot源码的 instanceOop.hpp 文件中,另外,arrayOopDesc 的定义对应 arrayOop.hpp
|
当一个线程尝试访问synchronized修饰的代码块时,它首先要获得锁,那么这个锁到底存在哪里呢?是 存在锁对象的对象头中的。 HotSpot采用instanceOopDesc和arrayOopDesc来描述对象头,arrayOopDesc对象用来描述数组类型。instanceOopDesc的定义的在Hotspot源码的 instanceOop.hpp 文件中,另外,arrayOopDesc 的定义对应 arrayOop.hpp
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0022.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0022.png">
|
||||||
|
|
||||||
从instanceOopDesc代码中可以看到 instanceOopDesc继承自oopDesc,oopDesc的定义载Hotspot 源码中的 oop.hpp 文件中。
|
从instanceOopDesc代码中可以看到 instanceOopDesc继承自oopDesc,oopDesc的定义载Hotspot 源码中的 oop.hpp 文件中。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0023.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0023.png">
|
||||||
|
|
||||||
- 在普通实例对象中,oopDesc的定义包含两个成员,分别是 _mark 和 _metadata
|
- 在普通实例对象中,oopDesc的定义包含两个成员,分别是 _mark 和 _metadata
|
||||||
|
|
||||||
@@ -383,21 +383,21 @@ monitorexit释放锁。 monitorexit插入在方法结束处和异常处,JVM保
|
|||||||
|
|
||||||
Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、 线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。Mark Word对应的类 型是 markOop 。源码位于 markOop.hpp 中。
|
Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、 线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。Mark Word对应的类 型是 markOop 。源码位于 markOop.hpp 中。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0024.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0024.png">
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0025.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0025.png">
|
||||||
|
|
||||||
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:
|
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0026.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0026.png">
|
||||||
|
|
||||||
在32位虚拟机下,Mark Word是32bit大小的,其存储结构如下:
|
在32位虚拟机下,Mark Word是32bit大小的,其存储结构如下:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0027.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0027.png">
|
||||||
|
|
||||||
再加一个图对比一下,有一丁点的补充
|
再加一个图对比一下,有一丁点的补充
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0028.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0028.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)
|
|||||||
|
|
||||||
线程在执行同步块之前,JVM会先在当前的线程的栈帧中创建一个`Lock Record`,其包括一个用于存储对象头中的 `mark word`(官方称之为`Displaced Mark Word`)以及一个指向对象的指针。下图右边的部分就是一个`Lock Record`。
|
线程在执行同步块之前,JVM会先在当前的线程的栈帧中创建一个`Lock Record`,其包括一个用于存储对象头中的 `mark word`(官方称之为`Displaced Mark Word`)以及一个指向对象的指针。下图右边的部分就是一个`Lock Record`。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0029.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0029.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -571,7 +571,7 @@ synchronized(obj){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0030.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0030.png">
|
||||||
|
|
||||||
## 轻量级锁什么时候升级为重量级锁?
|
## 轻量级锁什么时候升级为重量级锁?
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ synchronized(obj){
|
|||||||
|
|
||||||
- 重量级锁的状态下,对象的`mark word`为指向一个堆中monitor对象的指针。一个monitor对象包括这么几个关键字段:cxq(下图中的ContentionList),EntryList ,WaitSet,owner。其中cxq ,EntryList ,WaitSet都是由ObjectWaiter的链表结构,owner指向持有锁的线程。
|
- 重量级锁的状态下,对象的`mark word`为指向一个堆中monitor对象的指针。一个monitor对象包括这么几个关键字段:cxq(下图中的ContentionList),EntryList ,WaitSet,owner。其中cxq ,EntryList ,WaitSet都是由ObjectWaiter的链表结构,owner指向持有锁的线程。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0031.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0031.png">
|
||||||
|
|
||||||
在HotSpot虚拟机中,monitor是由ObjectMonitor实现的。其源码是用c++来实现的,位于HotSpot虚 拟机源码ObjectMonitor.hpp文件中(src/share/vm/runtime/objectMonitor.hpp)。ObjectMonitor主 要数据结构如下:
|
在HotSpot虚拟机中,monitor是由ObjectMonitor实现的。其源码是用c++来实现的,位于HotSpot虚 拟机源码ObjectMonitor.hpp文件中(src/share/vm/runtime/objectMonitor.hpp)。ObjectMonitor主 要数据结构如下:
|
||||||
|
|
||||||
@@ -661,7 +661,7 @@ ObjectMonitor() {
|
|||||||
|
|
||||||
1、执行monitorenter时,会调用InterpreterRuntime.cpp (位于:src/share/vm/interpreter/interpreterRuntime.cpp) 的 InterpreterRuntime::monitorenter函 数。具体代码可参见HotSpot源码。
|
1、执行monitorenter时,会调用InterpreterRuntime.cpp (位于:src/share/vm/interpreter/interpreterRuntime.cpp) 的 InterpreterRuntime::monitorenter函 数。具体代码可参见HotSpot源码。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0032.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0032.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1147,7 +1147,7 @@ if (TryLock(Self) > 0) break ;
|
|||||||
|
|
||||||
可以看到ObjectMonitor的函数调用中会涉及到Atomic::cmpxchg_ptr,Atomic::inc_ptr等内核函数, 执行同步代码块,没有竞争到锁的对象会park()被挂起,竞争到锁的线程会unpark()唤醒。这个时候就 会存在操作系统用户态和内核态的转换,这种切换会消耗大量的系统资源。所以synchronized是Java语 言中是一个重量级(Heavyweight)的操作。 用户态和和内核态是什么东西呢?要想了解用户态和内核态还需要先了解一下Linux系统的体系架构:
|
可以看到ObjectMonitor的函数调用中会涉及到Atomic::cmpxchg_ptr,Atomic::inc_ptr等内核函数, 执行同步代码块,没有竞争到锁的对象会park()被挂起,竞争到锁的线程会unpark()唤醒。这个时候就 会存在操作系统用户态和内核态的转换,这种切换会消耗大量的系统资源。所以synchronized是Java语 言中是一个重量级(Heavyweight)的操作。 用户态和和内核态是什么东西呢?要想了解用户态和内核态还需要先了解一下Linux系统的体系架构:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0033.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0033.png">
|
||||||
|
|
||||||
从上图可以看出,Linux操作系统的体系架构分为:用户空间(应用程序的活动空间)和内核。 内核:本质上可以理解为一种软件,控制计算机的硬件资源,并提供上层应用程序运行的环境。 用户空间:上层应用程序活动的空间。应用程序的执行必须依托于内核提供的资源,包括CPU资源、存 储资源、I/O资源等。 系统调用:为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。
|
从上图可以看出,Linux操作系统的体系架构分为:用户空间(应用程序的活动空间)和内核。 内核:本质上可以理解为一种软件,控制计算机的硬件资源,并提供上层应用程序运行的环境。 用户空间:上层应用程序活动的空间。应用程序的执行必须依托于内核提供的资源,包括CPU资源、存 储资源、I/O资源等。 系统调用:为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ categories:
|
|||||||
- 原理
|
- 原理
|
||||||
keywords: Java并发,AQS源码
|
keywords: Java并发,AQS源码
|
||||||
description: '万字系列长文讲解-Java并发体系-第四阶段-AQS源码解读-[1]。'
|
description: '万字系列长文讲解-Java并发体系-第四阶段-AQS源码解读-[1]。'
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/Java_concurrency.png'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png'
|
||||||
abbrlink: 92c4503d
|
abbrlink: 92c4503d
|
||||||
date: 2020-10-26 17:59:42
|
date: 2020-10-26 17:59:42
|
||||||
---
|
---
|
||||||
@@ -410,11 +410,11 @@ Process finished with exit code 0
|
|||||||
|
|
||||||
**技术翻译:**是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石, 通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量`state`表示持有锁的状态。
|
**技术翻译:**是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石, 通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量`state`表示持有锁的状态。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0011.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0011.png">
|
||||||
|
|
||||||
AbstractOwnableSynchronizer
|
AbstractOwnableSynchronizer
|
||||||
AbstractQueuedLongSynchronizer
|
AbstractQueuedLongSynchronizer
|
||||||
@@ -426,7 +426,7 @@ AbstractQueuedSynchronizer
|
|||||||
|
|
||||||
AQS是一个抽象的父类,可以将其理解为一个框架。基于AQS这个框架,我们可以实现多种同步器,比如下方图中的几个Java内置的同步器。同时我们也可以基于AQS框架实现我们自己的同步器以满足不同的业务场景需求。
|
AQS是一个抽象的父类,可以将其理解为一个框架。基于AQS这个框架,我们可以实现多种同步器,比如下方图中的几个Java内置的同步器。同时我们也可以基于AQS框架实现我们自己的同步器以满足不同的业务场景需求。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0002.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0002.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -434,7 +434,7 @@ AQS是一个抽象的父类,可以将其理解为一个框架。基于AQS这
|
|||||||
|
|
||||||
加锁会导致阻塞:有阻塞就需要排队,实现排队必然需要有某种形式的队列来进行管理
|
加锁会导致阻塞:有阻塞就需要排队,实现排队必然需要有某种形式的队列来进行管理
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0003.png">
|
||||||
|
|
||||||
1、抢到资源的线程直接使用办理业务,抢占不到资源的线程的必然涉及一种**排队等候机制**,抢占资源失败的线程继续去等待(类似办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。
|
1、抢到资源的线程直接使用办理业务,抢占不到资源的线程的必然涉及一种**排队等候机制**,抢占资源失败的线程继续去等待(类似办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ Node 的数据结构其实也挺简单的,就是 thread + waitStatus + pre + n
|
|||||||
|
|
||||||
## AQS队列基本结构
|
## AQS队列基本结构
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0004.png">
|
||||||
|
|
||||||
注意排队队列,不包括head(也就是后文要说的哨兵节点)。
|
注意排队队列,不包括head(也就是后文要说的哨兵节点)。
|
||||||
|
|
||||||
@@ -583,7 +583,7 @@ public class AQSDemo {
|
|||||||
|
|
||||||
以这样的一个实际例子说明。
|
以这样的一个实际例子说明。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0005.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -765,7 +765,7 @@ public class AQSDemo {
|
|||||||
|
|
||||||
2、C在if逻辑里准备入队,进行相应设置后,变成下面这样。
|
2、C在if逻辑里准备入队,进行相应设置后,变成下面这样。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0006.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0006.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -831,7 +831,7 @@ public class AQSDemo {
|
|||||||
|
|
||||||
此时队列变成了下面的样子:
|
此时队列变成了下面的样子:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0007.png">
|
||||||
|
|
||||||
3、然后if结束之后,继续空的for循环,B线程开始了第二轮循环。
|
3、然后if结束之后,继续空的for循环,B线程开始了第二轮循环。
|
||||||
|
|
||||||
@@ -843,11 +843,11 @@ public class AQSDemo {
|
|||||||
|
|
||||||
2、`node.prev = t`,进入if之后,让B节点的prev指针指向t,然后`compareAndSetTail(t, node)`设置尾节点
|
2、`node.prev = t`,进入if之后,让B节点的prev指针指向t,然后`compareAndSetTail(t, node)`设置尾节点
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0008.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0008.png">
|
||||||
|
|
||||||
3、CAS设置尾节点成功之后,执行if里的逻辑
|
3、CAS设置尾节点成功之后,执行if里的逻辑
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0009.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0009.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1197,7 +1197,7 @@ protected final void setExclusiveOwnerThread(Thread thread) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0010.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0010.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ categories:
|
|||||||
- 入门
|
- 入门
|
||||||
keywords: Netty
|
keywords: Netty
|
||||||
description: 第一话对BIO和NIO进行了讲解,为后续做准备。
|
description: 第一话对BIO和NIO进行了讲解,为后续做准备。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||||
abbrlink: 3f9283e7
|
abbrlink: 3f9283e7
|
||||||
date: 2021-04-08 14:21:58
|
date: 2021-04-08 14:21:58
|
||||||
---
|
---
|
||||||
@@ -33,7 +33,7 @@ date: 2021-04-08 14:21:58
|
|||||||
|
|
||||||
相对简单的一个体系图
|
相对简单的一个体系图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
|
||||||
|
|
||||||
## Netty 的应用场景
|
## Netty 的应用场景
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ date: 2021-04-08 14:21:58
|
|||||||
|
|
||||||
## Netty 的学习资料参考
|
## Netty 的学习资料参考
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -97,11 +97,11 @@ date: 2021-04-08 14:21:58
|
|||||||
2. `Java` 共支持 `3` 种网络编程模型 `I/O` 模式:`BIO`、`NIO`、`AIO`。
|
2. `Java` 共支持 `3` 种网络编程模型 `I/O` 模式:`BIO`、`NIO`、`AIO`。
|
||||||
3. `Java BIO`:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】
|
3. `Java BIO`:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
|
||||||
|
|
||||||
4. `Java NIO`:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 `I/O` 请求就进行处理。【简单示意图】
|
4. `Java NIO`:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 `I/O` 请求就进行处理。【简单示意图】
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
|
||||||
|
|
||||||
5. `Java AIO(NIO.2)`:异步非阻塞,`AIO` 引入异步通道的概念,采用了 `Proactor` 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
|
5. `Java AIO(NIO.2)`:异步非阻塞,`AIO` 引入异步通道的概念,采用了 `Proactor` 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
|
||||||
6. 我们依次展开讲解。
|
6. 我们依次展开讲解。
|
||||||
@@ -120,7 +120,7 @@ date: 2021-04-08 14:21:58
|
|||||||
|
|
||||||
## Java BIO 工作机制
|
## Java BIO 工作机制
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
|
||||||
|
|
||||||
对 `BIO` 编程流程的梳理
|
对 `BIO` 编程流程的梳理
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ public class BIOServer {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ public class BasicBuffer {
|
|||||||
3. `BIO` 基于字节流和字符流进行操作,而 `NIO` 基于 `Channel`(通道)和 `Buffer`(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。`Selector`(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
|
3. `BIO` 基于字节流和字符流进行操作,而 `NIO` 基于 `Channel`(通道)和 `Buffer`(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。`Selector`(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
|
||||||
4. Buffer和Channel之间的数据流向是双向的
|
4. Buffer和Channel之间的数据流向是双向的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
|
||||||
|
|
||||||
## NIO 三大核心原理示意图
|
## NIO 三大核心原理示意图
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ public class BasicBuffer {
|
|||||||
|
|
||||||
关系图的说明:
|
关系图的说明:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
|
||||||
|
|
||||||
1. 每个 `Channel` 都会对应一个 `Buffer`。
|
1. 每个 `Channel` 都会对应一个 `Buffer`。
|
||||||
2. `Selector` 对应一个线程,一个线程对应多个 `Channel`(连接)。
|
2. `Selector` 对应一个线程,一个线程对应多个 `Channel`(连接)。
|
||||||
@@ -303,17 +303,17 @@ public class BasicBuffer {
|
|||||||
|
|
||||||
缓冲区(`Buffer`):缓冲区本质上是一个**可以读写数据的内存块**,可以理解成是一个**容器对象(含数组)**,该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。`Channel` 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 `Buffer`,如图:【后面举例说明】
|
缓冲区(`Buffer`):缓冲区本质上是一个**可以读写数据的内存块**,可以理解成是一个**容器对象(含数组)**,该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。`Channel` 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 `Buffer`,如图:【后面举例说明】
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
|
||||||
|
|
||||||
### Buffer 类及其子类
|
### Buffer 类及其子类
|
||||||
|
|
||||||
1. 在 `NIO` 中,`Buffer` 是一个顶层父类,它是一个抽象类,类的层级关系图:
|
1. 在 `NIO` 中,`Buffer` 是一个顶层父类,它是一个抽象类,类的层级关系图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
|
||||||
|
|
||||||
2. `Buffer` 类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
|
2. `Buffer` 类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -321,13 +321,13 @@ public class BasicBuffer {
|
|||||||
|
|
||||||
3. `Buffer` 类相关方法一览
|
3. `Buffer` 类相关方法一览
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
|
||||||
|
|
||||||
### ByteBuffer
|
### ByteBuffer
|
||||||
|
|
||||||
从前面可以看出对于 `Java` 中的基本数据类型(`boolean` 除外),都有一个 `Buffer` 类型与之相对应,最常用的自然是 `ByteBuffer` 类(二进制数据),该类的主要方法如下:
|
从前面可以看出对于 `Java` 中的基本数据类型(`boolean` 除外),都有一个 `Buffer` 类型与之相对应,最常用的自然是 `ByteBuffer` 类(二进制数据),该类的主要方法如下:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
|
||||||
|
|
||||||
## 通道(Channel)
|
## 通道(Channel)
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ public class BasicBuffer {
|
|||||||
5. `FileChannel` 用于文件的数据读写,`DatagramChannel` 用于 `UDP` 的数据读写,`ServerSocketChannel` 和 `SocketChannel` 用于 `TCP` 的数据读写。
|
5. `FileChannel` 用于文件的数据读写,`DatagramChannel` 用于 `UDP` 的数据读写,`ServerSocketChannel` 和 `SocketChannel` 用于 `TCP` 的数据读写。
|
||||||
6. 图示
|
6. 图示
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
|
||||||
|
|
||||||
### FileChannel 类
|
### FileChannel 类
|
||||||
|
|
||||||
@@ -444,7 +444,7 @@ public class NIOFileChannel02 {
|
|||||||
2. 拷贝一个文本文件 `1.txt`,放在项目下即可
|
2. 拷贝一个文本文件 `1.txt`,放在项目下即可
|
||||||
3. 代码演示
|
3. 代码演示
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
package com.atguigu.nio;
|
package com.atguigu.nio;
|
||||||
@@ -721,7 +721,7 @@ public class ScatteringAndGatheringTest {
|
|||||||
|
|
||||||
### Selector 示意图和特点说明
|
### Selector 示意图和特点说明
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
|
||||||
|
|
||||||
说明如下:
|
说明如下:
|
||||||
|
|
||||||
@@ -733,7 +733,7 @@ public class ScatteringAndGatheringTest {
|
|||||||
|
|
||||||
### Selector 类相关方法
|
### Selector 类相关方法
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
|
||||||
|
|
||||||
### 注意事项
|
### 注意事项
|
||||||
|
|
||||||
@@ -748,7 +748,7 @@ public class ScatteringAndGatheringTest {
|
|||||||
|
|
||||||
`NIO` 非阻塞网络编程相关的(`Selector`、`SelectionKey`、`ServerScoketChannel` 和 `SocketChannel`)关系梳理图
|
`NIO` 非阻塞网络编程相关的(`Selector`、`SelectionKey`、`ServerScoketChannel` 和 `SocketChannel`)关系梳理图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
|
||||||
|
|
||||||
对上图的说明:
|
对上图的说明:
|
||||||
|
|
||||||
@@ -938,21 +938,21 @@ public static final int OP_ACCEPT = 1 << 4;
|
|||||||
|
|
||||||
2. `SelectionKey` 相关方法
|
2. `SelectionKey` 相关方法
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
|
||||||
|
|
||||||
### ServerSocketChannel
|
### ServerSocketChannel
|
||||||
|
|
||||||
1. `ServerSocketChannel` 在服务器端监听新的客户端 `Socket` 连接,负责监听,不负责实际的读写操作
|
1. `ServerSocketChannel` 在服务器端监听新的客户端 `Socket` 连接,负责监听,不负责实际的读写操作
|
||||||
2. 相关方法如下
|
2. 相关方法如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
|
||||||
|
|
||||||
### SocketChannel
|
### SocketChannel
|
||||||
|
|
||||||
1. `SocketChannel`,网络 `IO` 通道,**具体负责进行读写操作**。`NIO` 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
|
1. `SocketChannel`,网络 `IO` 通道,**具体负责进行读写操作**。`NIO` 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
|
||||||
2. 相关方法如下
|
2. 相关方法如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
|
||||||
|
|
||||||
## NIO网络编程应用实例 - 群聊系统
|
## NIO网络编程应用实例 - 群聊系统
|
||||||
|
|
||||||
@@ -965,7 +965,7 @@ public static final int OP_ACCEPT = 1 << 4;
|
|||||||
5. 目的:进一步理解 `NIO` 非阻塞网络编程机制
|
5. 目的:进一步理解 `NIO` 非阻塞网络编程机制
|
||||||
6. 示意图分析和代码
|
6. 示意图分析和代码
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
|
||||||
|
|
||||||
代码:
|
代码:
|
||||||
|
|
||||||
@@ -1243,7 +1243,7 @@ socket.getOutputStream().write(arr);
|
|||||||
|
|
||||||
### 传统 IO 模型
|
### 传统 IO 模型
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
|
||||||
|
|
||||||
**DMA**:`direct memory access` 直接内存拷贝(不使用 `CPU`)
|
**DMA**:`direct memory access` 直接内存拷贝(不使用 `CPU`)
|
||||||
|
|
||||||
@@ -1252,19 +1252,19 @@ socket.getOutputStream().write(arr);
|
|||||||
1. `mmap` 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图
|
1. `mmap` 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图
|
||||||
2. `mmap` 示意图
|
2. `mmap` 示意图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
|
||||||
|
|
||||||
### sendFile 优化
|
### sendFile 优化
|
||||||
|
|
||||||
1. `Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`,同时,由于和用户态完全无关,就减少了一次上下文切换
|
1. `Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`,同时,由于和用户态完全无关,就减少了一次上下文切换
|
||||||
2. 示意图和小结
|
2. 示意图和小结
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
|
||||||
|
|
||||||
3. 提示:零拷贝从操作系统角度,是没有 `cpu` 拷贝
|
3. 提示:零拷贝从操作系统角度,是没有 `cpu` 拷贝
|
||||||
4. `Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图和小结:
|
4. `Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图和小结:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
|
||||||
|
|
||||||
5. 这里其实有一次 `cpu` 拷贝 `kernel buffer` -> `socket buffer` 但是,拷贝的信息很少,比如 `lenght`、`offset` 消耗低,可以忽略
|
5. 这里其实有一次 `cpu` 拷贝 `kernel buffer` -> `socket buffer` 但是,拷贝的信息很少,比如 `lenght`、`offset` 消耗低,可以忽略
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ categories:
|
|||||||
- 入门
|
- 入门
|
||||||
keywords: Netty
|
keywords: Netty
|
||||||
description: 对前面两话一些迷惑的点进行细说,讲解handler调用机制,TCP粘包,以及用netty写一个十分简单的RPC。
|
description: 对前面两话一些迷惑的点进行细说,讲解handler调用机制,TCP粘包,以及用netty写一个十分简单的RPC。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||||
abbrlink: 429acc6d
|
abbrlink: 429acc6d
|
||||||
date: 2021-04-21 17:38:58
|
date: 2021-04-21 17:38:58
|
||||||
---
|
---
|
||||||
@@ -25,7 +25,7 @@ date: 2021-04-21 17:38:58
|
|||||||
1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
|
1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
|
||||||
2. `codec`(编解码器)的组成部分有两个:`decoder`(解码器)和 `encoder`(编码器)。`encoder` 负责把业务数据转换成字节码数据,`decoder` 负责把字节码数据转换成业务数据
|
2. `codec`(编解码器)的组成部分有两个:`decoder`(解码器)和 `encoder`(编码器)。`encoder` 负责把业务数据转换成字节码数据,`decoder` 负责把字节码数据转换成业务数据
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
|
||||||
|
|
||||||
## Netty 本身的编码解码的机制和问题分析
|
## Netty 本身的编码解码的机制和问题分析
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ date: 2021-04-21 17:38:58
|
|||||||
8. 然后通过 `protoc.exe` 编译器根据 `.proto` 自动生成 `.java` 文件
|
8. 然后通过 `protoc.exe` 编译器根据 `.proto` 自动生成 `.java` 文件
|
||||||
9. `protobuf` 使用示意图
|
9. `protobuf` 使用示意图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
|
||||||
|
|
||||||
## Protobuf 快速入门实例
|
## Protobuf 快速入门实例
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ message Student { //会在 StudentPOJO 外部类生成一个内部类 Student,
|
|||||||
protoc.exe --java_out=.Student.proto
|
protoc.exe --java_out=.Student.proto
|
||||||
将生成的 StudentPOJO 放入到项目使用
|
将生成的 StudentPOJO 放入到项目使用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
|
||||||
|
|
||||||
生成的StudentPOJO代码太长就不贴在这里了
|
生成的StudentPOJO代码太长就不贴在这里了
|
||||||
|
|
||||||
@@ -676,7 +676,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
|||||||
2. `ChannelHandler` 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 `ChannelInboundHandler` 接口(或 `ChannelInboundHandlerAdapter`),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 `ChannelInboundHandler` 冲刷数据。业务逻辑通常写在一个或者多个 `ChannelInboundHandler` 中。`ChannelOutboundHandler` 原理一样,只不过它是用来处理出站数据的
|
2. `ChannelHandler` 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 `ChannelInboundHandler` 接口(或 `ChannelInboundHandlerAdapter`),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 `ChannelInboundHandler` 冲刷数据。业务逻辑通常写在一个或者多个 `ChannelInboundHandler` 中。`ChannelOutboundHandler` 原理一样,只不过它是用来处理出站数据的
|
||||||
3. `ChannelPipeline` 提供了 `ChannelHandler` 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 `pipeline` 中的一系列 `ChannelOutboundHandler`,并被这些 `Handler` 处理,反之则称为入站的
|
3. `ChannelPipeline` 提供了 `ChannelHandler` 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 `pipeline` 中的一系列 `ChannelOutboundHandler`,并被这些 `Handler` 处理,反之则称为入站的
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
|
||||||
|
|
||||||
> 出站,入站如果搞不清楚,看下面的**Netty的handler链的调用机制**,通过一个例子和图讲清楚
|
> 出站,入站如果搞不清楚,看下面的**Netty的handler链的调用机制**,通过一个例子和图讲清楚
|
||||||
|
|
||||||
@@ -689,12 +689,12 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
1. 关系继承图
|
1. 关系继承图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
|
||||||
|
|
||||||
2. 由于不可能知道远程节点是否会一次性发送一个完整的信息,`tcp` 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.【后面有说TCP的粘包和拆包问题】
|
2. 由于不可能知道远程节点是否会一次性发送一个完整的信息,`tcp` 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.【后面有说TCP的粘包和拆包问题】
|
||||||
3. 一个关于 `ByteToMessageDecoder` 实例分析
|
3. 一个关于 `ByteToMessageDecoder` 实例分析
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -710,7 +710,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
> 读者可以看下这个图,带着这个图去看下面的例子。
|
> 读者可以看下这个图,带着这个图去看下面的例子。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -978,11 +978,11 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
|||||||
|
|
||||||
### 效果
|
### 效果
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1002,14 +1002,10 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> 下面是Netty官方源码给的图,我个人觉的不是太好理解,上面的图好理解一些
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## ByteToMessageDecoder的小细节
|
## ByteToMessageDecoder的小细节
|
||||||
|
|
||||||
|
|
||||||
@@ -1090,13 +1086,13 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
如下图验证结果:
|
如下图验证结果:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. 同时又引出了一个小问题
|
2. 同时又引出了一个小问题
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
|
||||||
|
|
||||||
当我们`MyClientHandler`传一个Long时,会调用我们的`MyLongToByteEncoder`的编码器。那么控制台就会打印这样一句话:**MyLongToByteEncoder encode 被调用**。但是这里并没有调用编码器,这是为什么呢?
|
当我们`MyClientHandler`传一个Long时,会调用我们的`MyLongToByteEncoder`的编码器。那么控制台就会打印这样一句话:**MyLongToByteEncoder encode 被调用**。但是这里并没有调用编码器,这是为什么呢?
|
||||||
|
|
||||||
@@ -1149,7 +1145,7 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
|||||||
ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd",CharsetUtil.UTF_8));
|
ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd",CharsetUtil.UTF_8));
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1198,7 +1194,7 @@ public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> {
|
|||||||
|
|
||||||
## 其它编解码器
|
## 其它编解码器
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1249,7 +1245,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
|||||||
|
|
||||||
3. 演示整合
|
3. 演示整合
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1261,7 +1257,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
|||||||
2. 由于 `TCP` 无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题,看一张图
|
2. 由于 `TCP` 无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题,看一张图
|
||||||
3. `TCP` 粘包、拆包图解
|
3. `TCP` 粘包、拆包图解
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
|
||||||
|
|
||||||
|
|
||||||
假设客户端分别发送了两个数据包 `D1` 和 `D2` 给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:
|
假设客户端分别发送了两个数据包 `D1` 和 `D2` 给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:
|
||||||
@@ -1502,11 +1498,11 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
|||||||
|
|
||||||
**Client**
|
**Client**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
|
||||||
|
|
||||||
**Server**
|
**Server**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1514,13 +1510,13 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
|||||||
|
|
||||||
**Client**
|
**Client**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Server**
|
**Server**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1538,7 +1534,7 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
|||||||
1. 要求客户端发送 `5` 个 `Message` 对象,客户端每次发送一个 `Message` 对象
|
1. 要求客户端发送 `5` 个 `Message` 对象,客户端每次发送一个 `Message` 对象
|
||||||
2. 服务器端每次接收一个 `Message`,分 `5` 次进行解码,每读取到一个 `Message`,会回复一个 `Message` 对象给客户端。
|
2. 服务器端每次接收一个 `Message`,分 `5` 次进行解码,每读取到一个 `Message`,会回复一个 `Message` 对象给客户端。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1996,7 +1992,7 @@ MyMessageEncoder encode 方法被调用
|
|||||||
1. `RPC(Remote Procedure Call)`—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
|
1. `RPC(Remote Procedure Call)`—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
|
||||||
2. 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样(如图)
|
2. 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样(如图)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
|
||||||
|
|
||||||
过程:
|
过程:
|
||||||
|
|
||||||
@@ -2018,11 +2014,11 @@ MyMessageEncoder encode 方法被调用
|
|||||||
|
|
||||||
3. 常见的 `RPC` 框架有:比较知名的如阿里的 `Dubbo`、`Google` 的 `gRPC`、`Go` 语言的 `rpcx`、`Apache` 的 `thrift`,`Spring` 旗下的 `SpringCloud`。
|
3. 常见的 `RPC` 框架有:比较知名的如阿里的 `Dubbo`、`Google` 的 `gRPC`、`Go` 语言的 `rpcx`、`Apache` 的 `thrift`,`Spring` 旗下的 `SpringCloud`。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
|
||||||
|
|
||||||
## 我们的RPC 调用流程图
|
## 我们的RPC 调用流程图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
|
||||||
|
|
||||||
**RPC 调用流程说明**
|
**RPC 调用流程说明**
|
||||||
|
|
||||||
@@ -2052,7 +2048,7 @@ MyMessageEncoder encode 方法被调用
|
|||||||
3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 `Netty` 请求提供者返回数据
|
3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 `Netty` 请求提供者返回数据
|
||||||
4. 开发的分析图
|
4. 开发的分析图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ categories:
|
|||||||
- 入门
|
- 入门
|
||||||
keywords: Netty
|
keywords: Netty
|
||||||
description: 对Netty的架构进行了解析,主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。
|
description: 对Netty的架构进行了解析,主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||||
abbrlink: f846f3f
|
abbrlink: f846f3f
|
||||||
date: 2021-04-15 14:21:58
|
date: 2021-04-15 14:21:58
|
||||||
---
|
---
|
||||||
@@ -29,7 +29,7 @@ date: 2021-04-15 14:21:58
|
|||||||
|
|
||||||
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
|
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
|
||||||
|
|
||||||
## Netty 的优点
|
## Netty 的优点
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
1. 当并发数很大,就会创建大量的线程,占用很大系统资源
|
1. 当并发数很大,就会创建大量的线程,占用很大系统资源
|
||||||
2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的`read` 操作,导致上面的处理线程资源浪费
|
2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的`read` 操作,导致上面的处理线程资源浪费
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
|
||||||
|
|
||||||
## Reactor 模式
|
## Reactor 模式
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
1. 基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。(解决了当并发数很大时,会创建大量线程,占用很大系统资源)
|
1. 基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。(解决了当并发数很大时,会创建大量线程,占用很大系统资源)
|
||||||
2. 基于 `I/O` 复用模型:多个客户端进行连接,先把连接请求给`ServiceHandler`。多个连接共用一个阻塞对象`ServiceHandler`。假设,当C1连接没有数据要处理时,C1客户端只需要阻塞于`ServiceHandler`,C1之前的处理线程便可以处理其他有数据的连接,不会造成线程资源的浪费。当C1连接再次有数据时,`ServiceHandler`根据线程池的空闲状态,将请求分发给空闲的线程来处理C1连接的任务。(解决了线程资源浪费的那个问题)
|
2. 基于 `I/O` 复用模型:多个客户端进行连接,先把连接请求给`ServiceHandler`。多个连接共用一个阻塞对象`ServiceHandler`。假设,当C1连接没有数据要处理时,C1客户端只需要阻塞于`ServiceHandler`,C1之前的处理线程便可以处理其他有数据的连接,不会造成线程资源的浪费。当C1连接再次有数据时,`ServiceHandler`根据线程池的空闲状态,将请求分发给空闲的线程来处理C1连接的任务。(解决了线程资源浪费的那个问题)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
### I/O 复用结合线程池,就是 Reactor 模式基本设计思想,如图
|
### I/O 复用结合线程池,就是 Reactor 模式基本设计思想,如图
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
|
||||||
|
|
||||||
对上图说明:
|
对上图说明:
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
原理图,并使用 `NIO` 群聊系统验证
|
原理图,并使用 `NIO` 群聊系统验证
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
|
||||||
|
|
||||||
### 方案说明
|
### 方案说明
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
### 方案说明
|
### 方案说明
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -178,11 +178,11 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
针对单 `Reactor` 多线程模型中,`Reactor` 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 `Reactor` 在多线程中运行
|
针对单 `Reactor` 多线程模型中,`Reactor` 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 `Reactor` 在多线程中运行
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
|
||||||
|
|
||||||
> SubReactor是可以有多个的,如果只有一个SubReactor的话那和`单 Reactor 多线程`就没什么区别了。
|
> SubReactor是可以有多个的,如果只有一个SubReactor的话那和`单 Reactor 多线程`就没什么区别了。
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
### Scalable IO in Java 对 Multiple Reactors 的原理图解
|
### Scalable IO in Java 对 Multiple Reactors 的原理图解
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
|
||||||
|
|
||||||
### 方案优缺点说明
|
### 方案优缺点说明
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
`Netty` 主要基于主从 `Reactors` 多线程模型(如图)做了一定的改进,其中主从 `Reactor` 多线程模型有多个 `Reactor`
|
`Netty` 主要基于主从 `Reactors` 多线程模型(如图)做了一定的改进,其中主从 `Reactor` 多线程模型有多个 `Reactor`
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
|
||||||
|
|
||||||
**对上图说明**
|
**对上图说明**
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
### 工作原理示意图2 - 进阶版
|
### 工作原理示意图2 - 进阶版
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
|||||||
|
|
||||||
### 工作原理示意图3 - 详细版
|
### 工作原理示意图3 - 详细版
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -669,9 +669,9 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
下面第一张图就是管道,中间会经过多个handler
|
下面第一张图就是管道,中间会经过多个handler
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
|
||||||
|
|
||||||
说明:
|
说明:
|
||||||
|
|
||||||
@@ -917,11 +917,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
2. `ChannelHandler` 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
|
2. `ChannelHandler` 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
|
||||||
3. `ChannelHandler` 及其实现类一览图(后)
|
3. `ChannelHandler` 及其实现类一览图(后)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
|
||||||
|
|
||||||
4. 我们经常需要自定义一个 `Handler` 类去继承 `ChannelInboundHandlerAdapter`,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法
|
4. 我们经常需要自定义一个 `Handler` 类去继承 `ChannelInboundHandlerAdapter`,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
|
||||||
|
|
||||||
## Pipeline 和 ChannelPipeline
|
## Pipeline 和 ChannelPipeline
|
||||||
|
|
||||||
@@ -931,7 +931,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
2. `ChannelPipeline` 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 `Channel` 中各个的 `ChannelHandler` 如何相互交互
|
2. `ChannelPipeline` 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 `Channel` 中各个的 `ChannelHandler` 如何相互交互
|
||||||
3. 在 `Netty` 中每个 `Channel` 都有且仅有一个 `ChannelPipeline` 与之对应,它们的组成关系如下
|
3. 在 `Netty` 中每个 `Channel` 都有且仅有一个 `ChannelPipeline` 与之对应,它们的组成关系如下
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
|
||||||
|
|
||||||
4. 常用方法
|
4. 常用方法
|
||||||
`ChannelPipeline addFirst(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的第一个位置`ChannelPipeline addLast(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的最后一个位置
|
`ChannelPipeline addFirst(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的第一个位置`ChannelPipeline addLast(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的最后一个位置
|
||||||
@@ -940,7 +940,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
|
|
||||||
从head看一下debug
|
从head看一下debug
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
|
||||||
|
|
||||||
- `TestServerInitializer`和`HttpServerCodec`这些东西本身也是`handler`
|
- `TestServerInitializer`和`HttpServerCodec`这些东西本身也是`handler`
|
||||||
- 一般来说事件从客户端往服务器走我们称为出站,反之则是入站。
|
- 一般来说事件从客户端往服务器走我们称为出站,反之则是入站。
|
||||||
@@ -950,7 +950,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
1. 保存 `Channel` 相关的所有上下文信息,同时关联一个 `ChannelHandler` 对象
|
1. 保存 `Channel` 相关的所有上下文信息,同时关联一个 `ChannelHandler` 对象
|
||||||
2. 即 `ChannelHandlerContext` 中包含一个具体的事件处理器 `ChannelHandler`,同时 `ChannelHandlerContext` 中也绑定了对应的 `pipeline` 和 `Channel` 的信息,方便对 `ChannelHandler` 进行调用。
|
2. 即 `ChannelHandlerContext` 中包含一个具体的事件处理器 `ChannelHandler`,同时 `ChannelHandlerContext` 中也绑定了对应的 `pipeline` 和 `Channel` 的信息,方便对 `ChannelHandler` 进行调用。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
|
||||||
|
|
||||||
3. 常用方法
|
3. 常用方法
|
||||||
|
|
||||||
@@ -959,14 +959,14 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
- `ChannelFuture writeAndFlush(Object msg)`,将数据写到
|
- `ChannelFuture writeAndFlush(Object msg)`,将数据写到
|
||||||
- `ChannelPipeline` 中当前 `ChannelHandler` 的下一个 `ChannelHandler` 开始处理(出站)
|
- `ChannelPipeline` 中当前 `ChannelHandler` 的下一个 `ChannelHandler` 开始处理(出站)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
|
||||||
|
|
||||||
## ChannelOption
|
## ChannelOption
|
||||||
|
|
||||||
1. `Netty` 在创建 `Channel` 实例后,一般都需要设置 `ChannelOption` 参数。
|
1. `Netty` 在创建 `Channel` 实例后,一般都需要设置 `ChannelOption` 参数。
|
||||||
2. `ChannelOption` 参数如下:
|
2. `ChannelOption` 参数如下:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
|
||||||
|
|
||||||
## EventLoopGroup 和其实现类 NioEventLoopGroup
|
## EventLoopGroup 和其实现类 NioEventLoopGroup
|
||||||
|
|
||||||
@@ -974,7 +974,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
2. `EventLoopGroup` 提供 `next` 接口,可以从组里面按照一定规则获取其中一个 `EventLoop` 来处理任务。在 `Netty` 服务器端编程中,我们一般都需要提供两个 `EventLoopGroup`,例如:`BossEventLoopGroup` 和 `WorkerEventLoopGroup`。
|
2. `EventLoopGroup` 提供 `next` 接口,可以从组里面按照一定规则获取其中一个 `EventLoop` 来处理任务。在 `Netty` 服务器端编程中,我们一般都需要提供两个 `EventLoopGroup`,例如:`BossEventLoopGroup` 和 `WorkerEventLoopGroup`。
|
||||||
3. 通常一个服务端口即一个 `ServerSocketChannel` 对应一个 `Selector` 和一个 `EventLoop` 线程。`BossEventLoop` 负责接收客户端的连接并将 `SocketChannel` 交给 `WorkerEventLoopGroup` 来进行 `IO` 处理,如下图所示
|
3. 通常一个服务端口即一个 `ServerSocketChannel` 对应一个 `Selector` 和一个 `EventLoop` 线程。`BossEventLoop` 负责接收客户端的连接并将 `SocketChannel` 交给 `WorkerEventLoopGroup` 来进行 `IO` 处理,如下图所示
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
|
||||||
|
|
||||||
4. 常用方法
|
4. 常用方法
|
||||||
`public NioEventLoopGroup()`,构造方法
|
`public NioEventLoopGroup()`,构造方法
|
||||||
@@ -985,11 +985,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
|||||||
1. `Netty` 提供一个专门用来操作缓冲区(即 `Netty` 的数据容器)的工具类
|
1. `Netty` 提供一个专门用来操作缓冲区(即 `Netty` 的数据容器)的工具类
|
||||||
2. 常用方法如下所示
|
2. 常用方法如下所示
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
|
||||||
|
|
||||||
3. 举例说明 `Unpooled` 获取 `Netty` 的数据容器 `ByteBuf` 的基本使用
|
3. 举例说明 `Unpooled` 获取 `Netty` 的数据容器 `ByteBuf` 的基本使用
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
|
||||||
|
|
||||||
案例 1
|
案例 1
|
||||||
|
|
||||||
@@ -1096,7 +1096,7 @@ public class NettyByteBuf02 {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
|
||||||
|
|
||||||
代码如下:
|
代码如下:
|
||||||
|
|
||||||
@@ -1523,7 +1523,7 @@ public class MyServerHandler extends ChannelInboundHandlerAdapter {
|
|||||||
4. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知
|
4. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知
|
||||||
5. 运行界面
|
5. 运行界面
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1724,7 +1724,7 @@ public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<Tex
|
|||||||
|
|
||||||
可以看到并不是发一次数据,连接就关闭了,而是可以继续发送。
|
可以看到并不是发一次数据,连接就关闭了,而是可以继续发送。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ categories:
|
|||||||
- 操作系统
|
- 操作系统
|
||||||
keywords: 操作系统,IO,零拷贝
|
keywords: 操作系统,IO,零拷贝
|
||||||
description: 基本面试会问到的IO进行了详解,同时本篇文章也对面试以及平时工作中会看到的零拷贝进行了充分的解析。万字长文系列,读到就是赚到。
|
description: 基本面试会问到的IO进行了详解,同时本篇文章也对面试以及平时工作中会看到的零拷贝进行了充分的解析。万字长文系列,读到就是赚到。
|
||||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/os_logo.jpg'
|
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/os_logo.jpg'
|
||||||
abbrlink: e959db2e
|
abbrlink: e959db2e
|
||||||
date: 2021-04-08 15:21:58
|
date: 2021-04-08 15:21:58
|
||||||
---
|
---
|
||||||
@@ -47,7 +47,7 @@ date: 2021-04-08 15:21:58
|
|||||||
3. 而在用户进程这边,整 个进程会被阻塞。当**内核**一直等到数据准备好了,它就会将数据从**内核**中拷贝到用户内存,然后**内核**返回果,用户进程才解除 block的状态,重新运行起来。
|
3. 而在用户进程这边,整 个进程会被阻塞。当**内核**一直等到数据准备好了,它就会将数据从**内核**中拷贝到用户内存,然后**内核**返回果,用户进程才解除 block的状态,重新运行起来。
|
||||||
4. **所以,blocking IO的特点就是在IO执行的两个阶段都被block了。**
|
4. **所以,blocking IO的特点就是在IO执行的两个阶段都被block了。**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0003.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/computer_network/summary/0003.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ date: 2021-04-08 15:21:58
|
|||||||
3. 虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
|
3. 虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
|
||||||
4. **所以,用户进程第一个阶段不是阻塞的,需要不断的主动询问内核数据好了没有;第二个阶段依然总是阻塞的。**
|
4. **所以,用户进程第一个阶段不是阻塞的,需要不断的主动询问内核数据好了没有;第二个阶段依然总是阻塞的。**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0004.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/computer_network/summary/0004.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ date: 2021-04-08 15:21:58
|
|||||||
2. 它的基本原理就是select /epoll这个函数会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程,正式发起read请求。
|
2. 它的基本原理就是select /epoll这个函数会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程,正式发起read请求。
|
||||||
3. 从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket(也就是数据准备好了的socket),即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
|
3. 从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket(也就是数据准备好了的socket),即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0005.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/computer_network/summary/0005.png">
|
||||||
|
|
||||||
**select函数**
|
**select函数**
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ date: 2021-04-08 15:21:58
|
|||||||
4. 通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时(就是数据准备好的时候),则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。
|
4. 通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时(就是数据准备好的时候),则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。
|
||||||
5. 由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。(一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。)
|
5. 由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。(一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。)
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0001.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0001.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ date: 2021-04-08 15:21:58
|
|||||||
3. 而另一方面,从**内核**的角度,当它受到一个异步读之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都 完成之后,**内核**会给用户进程发送一个信号,告诉它read操作完成了,用户线程直接使用即可。 在这整个过程中,进程完全没有被阻塞。
|
3. 而另一方面,从**内核**的角度,当它受到一个异步读之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都 完成之后,**内核**会给用户进程发送一个信号,告诉它read操作完成了,用户线程直接使用即可。 在这整个过程中,进程完全没有被阻塞。
|
||||||
4. 异步IO模型使用了Proactor设计模式实现了这一机制。**(具体怎么搞得,看上面的文章链接)**
|
4. 异步IO模型使用了Proactor设计模式实现了这一机制。**(具体怎么搞得,看上面的文章链接)**
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0007.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.8/computer_network/summary/0007.png">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ date: 2021-04-08 15:21:58
|
|||||||
|
|
||||||
1. 来看一个标准设备(不是真实存在的,相当于一个逻辑上抽象的东西),通过它来帮助我们更好地理解设备交互的机制。可以看到一个包含两部分重要组件的设备。第一部分是向系统其他部分展现的硬件接口(interface)。同软件一样,硬件也需要一些接口,让系统软件来控制它的操作。因此,所有设备都有自己的特定接口以及典型交互的协议。
|
1. 来看一个标准设备(不是真实存在的,相当于一个逻辑上抽象的东西),通过它来帮助我们更好地理解设备交互的机制。可以看到一个包含两部分重要组件的设备。第一部分是向系统其他部分展现的硬件接口(interface)。同软件一样,硬件也需要一些接口,让系统软件来控制它的操作。因此,所有设备都有自己的特定接口以及典型交互的协议。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0002.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0002.png"/>
|
||||||
|
|
||||||
2. 第2部分是它的内部结构(internal structure)。这部分包含设备相关的特定实现,负责具体实现设备展示给系统的抽象接口。
|
2. 第2部分是它的内部结构(internal structure)。这部分包含设备相关的特定实现,负责具体实现设备展示给系统的抽象接口。
|
||||||
|
|
||||||
@@ -231,11 +231,11 @@ While (STATUS == BUSY);//wait until device is done with your request
|
|||||||
|
|
||||||
1. 没有中断时:进程1在CPU上运行一段时间(对应CPU那一行上重复的1),然后发出一个读取数据的I/O请求给磁盘。如果没有中断,那么操作系统就会简单自旋,不断轮询设备状态,直到设备完成I/O操作(对应其中的p)。当设备完成请求的操作后,进程1又可以继续运行。
|
1. 没有中断时:进程1在CPU上运行一段时间(对应CPU那一行上重复的1),然后发出一个读取数据的I/O请求给磁盘。如果没有中断,那么操作系统就会简单自旋,不断轮询设备状态,直到设备完成I/O操作(对应其中的p)。当设备完成请求的操作后,进程1又可以继续运行。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0003.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0003.png"/>
|
||||||
|
|
||||||
2. 有了中断后:中断允许计算与I/O重叠(overlap),这是提高CPU利用率的关键。我们利用中断并允许重叠,操作系统就可以在等待磁盘操作时做其他事情。
|
2. 有了中断后:中断允许计算与I/O重叠(overlap),这是提高CPU利用率的关键。我们利用中断并允许重叠,操作系统就可以在等待磁盘操作时做其他事情。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0004.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0004.png"/>
|
||||||
|
|
||||||
- 在这个例子中,在磁盘处理进程1的请求时,操作系统在CPU上运行进程2。磁盘处理完成后,触发一个中断,然后操作系统唤醒进程1继续运行。这样,在这段时间,无论CPU还是磁盘都可以有效地利用。
|
- 在这个例子中,在磁盘处理进程1的请求时,操作系统在CPU上运行进程2。磁盘处理完成后,触发一个中断,然后操作系统唤醒进程1继续运行。这样,在这段时间,无论CPU还是磁盘都可以有效地利用。
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ While (STATUS == BUSY);//wait until device is done with your request
|
|||||||
|
|
||||||
中断仍旧存在的缺点:
|
中断仍旧存在的缺点:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0005.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0005.png"/>
|
||||||
|
|
||||||
IO过程简述:
|
IO过程简述:
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ IO过程简述:
|
|||||||
>
|
>
|
||||||
> 标准协议还有一点需要我们注意。具体来说,如果使用编程的I/O将一大块数据传给设备,CPU又会因为琐碎的任务而变得负载很重,浪费了时间和算力,本来更好是用于运行其他进程。下面的时间线展示了这个问题:
|
> 标准协议还有一点需要我们注意。具体来说,如果使用编程的I/O将一大块数据传给设备,CPU又会因为琐碎的任务而变得负载很重,浪费了时间和算力,本来更好是用于运行其他进程。下面的时间线展示了这个问题:
|
||||||
>
|
>
|
||||||
> <img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0006.png"/>
|
> <img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0006.png"/>
|
||||||
>
|
>
|
||||||
> 进程1在运行过程中需要向磁盘写一些数据,所以它开始进行I/O操作,将数据从内存拷贝到磁盘(其中标示c的过程)。**拷贝结束后,磁盘上的I/O操作开始执行,此时CPU才可以处理其他请求。**
|
> 进程1在运行过程中需要向磁盘写一些数据,所以它开始进行I/O操作,将数据从内存拷贝到磁盘(其中标示c的过程)。**拷贝结束后,磁盘上的I/O操作开始执行,此时CPU才可以处理其他请求。**
|
||||||
|
|
||||||
@@ -278,13 +278,13 @@ IO过程简述:
|
|||||||
>
|
>
|
||||||
> DMA工作过程如下。为了能够将数据传送给设备,操作系统会通过编程告诉DMA引擎数据在内存的位置,要拷贝的大小以及要拷贝到哪个设备。在此之后,操作系统就可以处理其他请求了。当DMA的任务完成后,DMA控制器会抛出一个中断来告诉操作系统自己已经完成数据传输。修改后的时间线如下:
|
> DMA工作过程如下。为了能够将数据传送给设备,操作系统会通过编程告诉DMA引擎数据在内存的位置,要拷贝的大小以及要拷贝到哪个设备。在此之后,操作系统就可以处理其他请求了。当DMA的任务完成后,DMA控制器会抛出一个中断来告诉操作系统自己已经完成数据传输。修改后的时间线如下:
|
||||||
>
|
>
|
||||||
> <img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0007.png"/>
|
> <img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0007.png"/>
|
||||||
>
|
>
|
||||||
> 从时间线中可以看到,数据的拷贝工作都是由DMA控制器来完成的。因为CPU在此时是空闲的,所以操作系统可以让它做一些其他事情,比如此处调度进程2到CPU来运行。因此进程2在进程1再次运行之前可以使用更多的CPU。
|
> 从时间线中可以看到,数据的拷贝工作都是由DMA控制器来完成的。因为CPU在此时是空闲的,所以操作系统可以让它做一些其他事情,比如此处调度进程2到CPU来运行。因此进程2在进程1再次运行之前可以使用更多的CPU。
|
||||||
|
|
||||||
为了更好理解,看图:
|
为了更好理解,看图:
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0008.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0008.png"/>
|
||||||
|
|
||||||
过程:
|
过程:
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ IO过程简述:
|
|||||||
|
|
||||||
场景:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。
|
场景:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0009.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0009.png"/>
|
||||||
|
|
||||||
1. 很明显发生了4次拷贝
|
1. 很明显发生了4次拷贝
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ IO过程简述:
|
|||||||
|
|
||||||
`read()` 系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,为了减少这一步开销,我们可以用 `mmap()` 替换 `read()` 系统调用函数。`mmap()` 系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样,操作系统内核与用户空间共享缓冲区,就不需要再进行任何的数据拷贝操作。
|
`read()` 系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,为了减少这一步开销,我们可以用 `mmap()` 替换 `read()` 系统调用函数。`mmap()` 系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样,操作系统内核与用户空间共享缓冲区,就不需要再进行任何的数据拷贝操作。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0010.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0010.png"/>
|
||||||
|
|
||||||
总的来说mmap减少了一次数据拷贝,总共4次上下文切换,3次数据拷贝
|
总的来说mmap减少了一次数据拷贝,总共4次上下文切换,3次数据拷贝
|
||||||
|
|
||||||
@@ -336,7 +336,7 @@ IO过程简述:
|
|||||||
|
|
||||||
`Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`
|
`Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0011.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0011.png"/>
|
||||||
|
|
||||||
总的来说有2次上下文切换,3次数据拷贝。
|
总的来说有2次上下文切换,3次数据拷贝。
|
||||||
|
|
||||||
@@ -344,7 +344,7 @@ IO过程简述:
|
|||||||
|
|
||||||
`Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝
|
`Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0012.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0012.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Spring源码纵览这一节,主要是先了解下Spring的一些核心东西
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926195844253.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926195844253.png" />
|
||||||
|
|
||||||
- **蓝色实线箭头**是指继承关系
|
- **蓝色实线箭头**是指继承关系
|
||||||
- **绿色虚线箭头**是指接口实现关系
|
- **绿色虚线箭头**是指接口实现关系
|
||||||
@@ -91,13 +91,13 @@ BeanPostProcessor
|
|||||||
|
|
||||||
快捷键:ctrl+F12 看类的方法
|
快捷键:ctrl+F12 看类的方法
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926232957909.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926232957909.png"/>
|
||||||
|
|
||||||
#### 实现类
|
#### 实现类
|
||||||
|
|
||||||
快捷键:ctrl+h 查看接口实现类
|
快捷键:ctrl+h 查看接口实现类
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926233308861.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926233308861.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ public interface ResourceLoader {
|
|||||||
|
|
||||||
#### 实现类
|
#### 实现类
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926234527992.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926234527992.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ public interface BeanFactory {
|
|||||||
|
|
||||||
> 源码分析小技巧:看源码时,我们可以先看一个类的接口继承关系,因为接口就是规范,大部分开源框架源码都是遵守这一规范的。
|
> 源码分析小技巧:看源码时,我们可以先看一个类的接口继承关系,因为接口就是规范,大部分开源框架源码都是遵守这一规范的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927103844753.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927103844753.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ public interface BeanFactory {
|
|||||||
|
|
||||||
#### AbstractApplicationContext
|
#### AbstractApplicationContext
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927104922679.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927104922679.png" />
|
||||||
|
|
||||||
环境类的意思就是谁持有这个策略;这里就解释了上文说ResourceLoader是环境类接口
|
环境类的意思就是谁持有这个策略;这里就解释了上文说ResourceLoader是环境类接口
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ public interface BeanFactory {
|
|||||||
|
|
||||||
#### GenericApplicationContext
|
#### GenericApplicationContext
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927105641446.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927105641446.png"/>
|
||||||
|
|
||||||
这里组合了DefaultListableBeanFactory
|
这里组合了DefaultListableBeanFactory
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927110806180.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927110806180.png" />
|
||||||
|
|
||||||
1. BeanDefinitionRegistry:Bean定义信息注册中心
|
1. BeanDefinitionRegistry:Bean定义信息注册中心
|
||||||
2. SimpleAliasRegistry:别名注册中心
|
2. SimpleAliasRegistry:别名注册中心
|
||||||
@@ -336,7 +336,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
|
|
||||||
#### 流程图-BeanDefinition注册流程
|
#### 流程图-BeanDefinition注册流程
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Spring%E6%BA%90%E7%A0%81-BeanDefinition%E6%B3%A8%E5%86%8C%E6%B5%81%E7%A8%8B.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Spring%E6%BA%90%E7%A0%81-BeanDefinition%E6%B3%A8%E5%86%8C%E6%B5%81%E7%A8%8B.png">
|
||||||
|
|
||||||
- 我们要看BeanDefinition是何时被放入到beanDefinitionMap,只需要在DefaultListableBeanFactory用到`beanDefinitionMap.put()`的地方打个断点。
|
- 我们要看BeanDefinition是何时被放入到beanDefinitionMap,只需要在DefaultListableBeanFactory用到`beanDefinitionMap.put()`的地方打个断点。
|
||||||
- 我们在DefaultListableBeanFactory里搜索,发现了registerBeanDefinition(注册Bean定义信息)这个方法名很像我们要找的东西,再看里面的代码,果然有`beanDefinitionMap.put()`这串代码,我们试着在这里打个断点
|
- 我们在DefaultListableBeanFactory里搜索,发现了registerBeanDefinition(注册Bean定义信息)这个方法名很像我们要找的东西,再看里面的代码,果然有`beanDefinitionMap.put()`这串代码,我们试着在这里打个断点
|
||||||
@@ -361,7 +361,7 @@ public class MainTest {
|
|||||||
|
|
||||||
#### Debug调用栈
|
#### Debug调用栈
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927142317864.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927142317864.png" />
|
||||||
|
|
||||||
> 调用栈的调用顺序已经非常清楚了,可以把图放大一点看,下面只说一些必要的信息。
|
> 调用栈的调用顺序已经非常清楚了,可以把图放大一点看,下面只说一些必要的信息。
|
||||||
|
|
||||||
@@ -780,7 +780,7 @@ public class MainTest {
|
|||||||
- 还有一个debug的猜测方向,想要注册BeanDefinition肯定要new,我们可以直接在AbstractBeanDefinition这个抽象父类的构造函数打断点,我们不知道会走哪个构造函数,所以给三个构造函数都打断点。
|
- 还有一个debug的猜测方向,想要注册BeanDefinition肯定要new,我们可以直接在AbstractBeanDefinition这个抽象父类的构造函数打断点,我们不知道会走哪个构造函数,所以给三个构造函数都打断点。
|
||||||
- 下面就是打完断点之后,运行MainTest测试类后的调用栈
|
- 下面就是打完断点之后,运行MainTest测试类后的调用栈
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927172855410.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927172855410.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -936,7 +936,7 @@ public class MainTest {
|
|||||||
|
|
||||||
### ApplicationContext接口功能
|
### ApplicationContext接口功能
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927175207106.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927175207106.png" />
|
||||||
|
|
||||||
1. ioc事件派发器
|
1. ioc事件派发器
|
||||||
2. 国际化解析
|
2. 国际化解析
|
||||||
@@ -969,7 +969,7 @@ public interface Aware {
|
|||||||
2. 注释的大致意思是:Aware是一个标记性的超接口(顶级接口),指示了一个Bean有资格通过回调方法的形式获取Spring容器底层组件。实际回调方法被定义在每一个子接口中,而且通常一个子接口只包含一个接口一个参数并且返回值为void的方法。
|
2. 注释的大致意思是:Aware是一个标记性的超接口(顶级接口),指示了一个Bean有资格通过回调方法的形式获取Spring容器底层组件。实际回调方法被定义在每一个子接口中,而且通常一个子接口只包含一个接口一个参数并且返回值为void的方法。
|
||||||
3. 说白了:只要实现了Aware子接口的Bean都能获取到一个Spring底层组件。
|
3. 说白了:只要实现了Aware子接口的Bean都能获取到一个Spring底层组件。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927191112023.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927191112023.png" />
|
||||||
|
|
||||||
比如实现了ApplicationContextAware接口,实现它的方法,就能通过回调机制拿到ApplicationContext
|
比如实现了ApplicationContextAware接口,实现它的方法,就能通过回调机制拿到ApplicationContext
|
||||||
|
|
||||||
@@ -979,7 +979,7 @@ public interface Aware {
|
|||||||
|
|
||||||
##### 流程图-Bean对象创建流程
|
##### 流程图-Bean对象创建流程
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Spring%E6%BA%90%E7%A0%81-Bean%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E6%B5%81%E7%A8%8B.png">
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Spring%E6%BA%90%E7%A0%81-Bean%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E6%B5%81%E7%A8%8B.png">
|
||||||
|
|
||||||
##### Debug调用栈
|
##### Debug调用栈
|
||||||
|
|
||||||
@@ -1055,7 +1055,7 @@ public class MainConfig {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927200211334.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927200211334.png" />
|
||||||
|
|
||||||
1. 有一些一样的东西不再赘述
|
1. 有一些一样的东西不再赘述
|
||||||
2. 在`AbstractApplicationContext#refresh()`里会调用
|
2. 在`AbstractApplicationContext#refresh()`里会调用
|
||||||
@@ -1267,7 +1267,7 @@ public class MainConfig {
|
|||||||
|
|
||||||
##### Debug调用栈
|
##### Debug调用栈
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927230734973.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927230734973.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1434,7 +1434,7 @@ public class MainTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928113235852.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928113235852.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1475,7 +1475,7 @@ public class MainTest {
|
|||||||
|
|
||||||
我们看到此时,Person的name属性还是null
|
我们看到此时,Person的name属性还是null
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928114746618.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928114746618.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1497,7 +1497,7 @@ public class MainTest {
|
|||||||
|
|
||||||
这里拿到属性值
|
这里拿到属性值
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115110820.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115110820.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1512,13 +1512,13 @@ public class MainTest {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115352416.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115352416.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bw就是上面说的 => 里面封装好了真正的Person对象
|
bw就是上面说的 => 里面封装好了真正的Person对象
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115702735.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115702735.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1528,7 +1528,7 @@ bw就是上面说的 => 里面封装好了真正的Person对象
|
|||||||
|
|
||||||
这里就是一层一层调,不重要跳过。
|
这里就是一层一层调,不重要跳过。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928120016581.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928120016581.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1549,7 +1549,7 @@ private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928141536440.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928141536440.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1588,7 +1588,7 @@ private class BeanPropertyHandler extends PropertyHandler {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143301161.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143301161.png"/>
|
||||||
|
|
||||||
最后就是反射走到了我们的`Person#setName(String name)`
|
最后就是反射走到了我们的`Person#setName(String name)`
|
||||||
|
|
||||||
@@ -1598,7 +1598,7 @@ private class BeanPropertyHandler extends PropertyHandler {
|
|||||||
|
|
||||||
### 再来看看messageSource何时赋值
|
### 再来看看messageSource何时赋值
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143910461.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143910461.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1742,7 +1742,7 @@ public class Person implements ApplicationContextAware, MessageSourceAware {
|
|||||||
|
|
||||||
老样子,只看不一样的调用栈
|
老样子,只看不一样的调用栈
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928151825366.png"/>
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928151825366.png"/>
|
||||||
|
|
||||||
### AbstractAutowireCapableBeanFactory#populateBean()
|
### AbstractAutowireCapableBeanFactory#populateBean()
|
||||||
|
|
||||||
@@ -1785,7 +1785,7 @@ public class Person implements ApplicationContextAware, MessageSourceAware {
|
|||||||
|
|
||||||
这里有一个非常著名的后置处理器,`AutowiredAnnotationBeanPostProcessor`自动装配注解后置处理器,顾名思义就是用来处理@Autowired注解自动装配的。
|
这里有一个非常著名的后置处理器,`AutowiredAnnotationBeanPostProcessor`自动装配注解后置处理器,顾名思义就是用来处理@Autowired注解自动装配的。
|
||||||
|
|
||||||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928153428691.png" />
|
<img src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928153428691.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user