cos测试完毕,图床切回原版

This commit is contained in:
youthlql
2022-03-16 01:23:41 +08:00
parent 8c89dac3bf
commit 0660078ffa
47 changed files with 737 additions and 737 deletions

View File

@@ -76,7 +76,7 @@ date: 2020-12-29 11:31:58
不过,解决一个问题的同时,往往会诞生出很多新的问题,所以微服务化的过程中伴随着很多的挑战,其中一个挑战就是有关服务(应用)配置的。当系统从一个单体应用,被拆分成分布式系统上一个个服务节点后,配置文件也必须跟着迁移(分割),这样配置就分散了,不仅如此,分散中还包含着冗余,如下图:
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0001.png"/>
配置中心将配置从应用中剥离出来,统一管理,优雅的解决了配置的动态变更、持久化、运维成本等问题。
@@ -86,7 +86,7 @@ date: 2020-12-29 11:31:58
在系统架构中,配置中心是整个微服务基础架构体系中的一个组件,如下图,它的功能看上去并不起眼,无非就是配置的管理和存取,但它是整个微服务架构中不可或缺的一环。
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0002.jpg"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0002.jpg"/>
@@ -171,7 +171,7 @@ Apollo简介
### Apollo简介
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0003.png"/>
**Apollo - A reliable configuration management system**
@@ -221,7 +221,7 @@ Apollo快速入门
### 执行流程
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0004.png"/>
操作流程如下:
@@ -262,7 +262,7 @@ Apollo的表结构对`timestamp`使用了多个default声明所以需要5.6.5
2. 打开1.3发布链接,下载必须的安装包:[https://github.com/ctripcorp/apollo/releases/tag/v1.3.0](https://github.com/ctripcorp/apollo/releases/tag/v1.3.0)。三个都要下
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0005.png"/>
解压安装包后将apollo-configservice-1.3.0.jar, apollo-adminservice-1.3.0.jar, apollo-portal-1.3.0.jar放置于apollo目录下
@@ -349,11 +349,11 @@ Apollo服务端共需要两个数据库`ApolloPortalDB`和`ApolloConfigDB`
1. 也可以使用提供的runApollo.bat快速启动三个服务修改数据库连接地址数据库以及密码
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0006.png"/>
这里面是一个很简单的脚本
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0007.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0007.png" />
@@ -385,29 +385,29 @@ start "ApolloPortal" java -Xms256m -Xmx256m -Dapollo_profile=github,auth -Ddev_m
1. 打开[apollo](http://localhost:8070/) 新建项目apollo-quickstart
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0008.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0008.png" />
2. 新建配置项sms.enable
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0009.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0009.png" />
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0010.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0010.png" />
确认提交配置项
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0011.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0011.png"/>
![image-20201228102339176](https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0012.png)
![image-20201228102339176](https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0012.png)
3. 发布配置项
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0013.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0013.png" />
#### 应用读取配置
@@ -485,7 +485,7 @@ public class GetConfigTest {
-Dapp.id=apollo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0014.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0014.png" />
运行GetConfigTest打开控制台观察输出结果
@@ -523,19 +523,19 @@ sma.enable: true
2. 运行GetConfigTest观察输出结果
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0015.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0015.png"/>
3. 在Apollo管理界面修改配置项
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0016.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0016.png"/>
4. 发布配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0017.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0017.png"/>
5. 在控制台查看详细情况可以看到程序获取的sms.enable的值已由false变成了修改后的true
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0018.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0018.png" />
@@ -546,7 +546,7 @@ Apollo应用
下图是Apollo架构模块的概览
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0019.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0019.png" />
#### 各模块职责
@@ -601,7 +601,7 @@ Apollo应用
它们的关系如下图所示:
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0020.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0020.png"/>
@@ -615,15 +615,15 @@ apollo 默认部门有两个。要增加自己的部门,可在系统参数中
* 进入系统参数设置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0021.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0021.png"/>
![image-20201228103939300](https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0022.png)
![image-20201228103939300](https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0022.png)
* 输入key查询已存在的部门设置organizations
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0023.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0023.png" />
* 修改value值来添加新部门下面添加一个微服务部门
@@ -639,11 +639,11 @@ apollo默认提供一个超级管理员: apollo可以自行添加用户
* 新建用户张三
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0024.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0024.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0025.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0025.png" />
#### 创建项目
@@ -661,7 +661,7 @@ apollo默认提供一个超级管理员: apollo可以自行添加用户
* 应用名称:应用名,仅用于界面展示
* 应用负责人选择的人默认会成为该项目的管理员具备项目权限管理、集群创建、Namespace创建等权限
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0026.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0026.png" />
4. 点击提交,创建成功后,会自动跳转到项目首页
@@ -671,7 +671,7 @@ apollo默认提供一个超级管理员: apollo可以自行添加用户
* 使用管理员apollo将指定项目授权给用户张三
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0027.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0027.png"/>
* 将修改和发布权限都授权给张三
@@ -731,7 +731,7 @@ Namespace作为配置的分类可当成一个配置文件。
进入项目首页点击左下脚的“添加Namespace”共包括两项关联公共Namespace和创建Namespace这里选择“创建Namespace”
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0028.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0028.png"/>
2. 添加配置项
@@ -758,11 +758,11 @@ Namespace作为配置的分类可当成一个配置文件。
进入common-template项目管理页面[http://localhost:8070/config.html?#/appid=common-template](http://localhost:8070/config.html?#/appid=common-template)
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0029.png" style="zoom:67%;" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0029.png" style="zoom:67%;" />
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0030.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0030.png" />
1. 添加配置项并发布
@@ -786,11 +786,11 @@ Namespace作为配置的分类可当成一个配置文件。
2. 点击左侧的添加Namespace
3. 添加Namespace
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0031.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0031.png"/>
4. 根据需求可以覆盖引入公共Namespace中的配置下面以覆盖server.servlet.context-path为例
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0032.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0032.png" />
5. 修改server.servlet.context-path为/account-service
6. 发布修改的配置项
@@ -806,15 +806,15 @@ Namespace作为配置的分类可当成一个配置文件。
1. 点击页面左侧的“添加集群”按钮
2. 输入集群名称SHAJQ选择环境并提交添加上海金桥数据中心为例
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0033.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0033.png" />
![image-20201228112150602](https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0034.png)
![image-20201228112150602](https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0034.png)
3. 切换到对应的集群,修改配置并发布即可
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0035.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0035.png" />
#### 同步集群配置
@@ -827,19 +827,19 @@ Namespace作为配置的分类可当成一个配置文件。
* 展开要同步的Namespace点击同步配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0036.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0036.png" />
![image-20201228112603903](https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0037.png)
![image-20201228112603903](https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0037.png)
* 选择同步到的新集群,再选择要同步的配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0038.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0038.png"/>
* 同步完成后切换到SHAJQ集群发布配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0039.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0039.png" />
#### 读取配置
@@ -863,7 +863,7 @@ Namespace作为配置的分类可当成一个配置文件。
在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的。
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0040.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0040.png" />
上图简要描述了配置发布的主要过程:
@@ -890,7 +890,7 @@ Admin Service在配置发布后需要通知所有的Config Service有配置
SELECT * FROM ApolloConfigDB.ReleaseMessage
```
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0041.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0041.png" />
@@ -1012,7 +1012,7 @@ Admin Service在配置发布后需要通知所有的Config Service有配置
4. NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后会通知对应的客户端
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0042.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0042.png"/>
#### Config Service通知客户端
@@ -1268,7 +1268,7 @@ Apollo客户端会把从服务端获取到的配置在本地文件系统缓存
-Denv=DEV -Dapollo.cacheDir=/opt/data/apollo-config -Dapollo.cluster=DEFAULTbash
```
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0043.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0043.png"/>
@@ -1366,11 +1366,11 @@ public class AccountApplication {
2. spring-http命名空间在之前已通过关联公共命名空间添加好了现在来添加spring-boot-druid命名空间
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0044.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0044.png" />
3. 添加本地文件中的配置到对应的命名空间,然后发布配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0045.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0045.png" />
4. 在account-service/src/main/resources/application.properties中配置apollo.bootstrap.namespaces需要引入的命名空间(上面写过)
@@ -1458,7 +1458,7 @@ public class AccountController {
* 访问[http://127.0.0.1:63000/account-service/db-url](http://127.0.0.1:63000/account-service/db-url),显示结果
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0046.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0046.png"/>
#### 创建其它项目
@@ -1474,7 +1474,7 @@ public class AccountController {
具体如下图所示:
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0047.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0047.png" />
下面以添加生产环境部署为例
@@ -1523,7 +1523,7 @@ UPDATE ServerConfig SET `Value` = "http://localhost:8081/eureka/" WHERE `key` =
服务配置项统一存储在ApolloPortalDB.ServerConfig表中可以通过`管理员工具 - 系统参数`页面进行配置apollo.portal.envs - 可支持的环境列表
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0048.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0048.png" />
默认值是dev如果portal需要管理多个环境的话以逗号分隔即可大小写不敏感
@@ -1556,7 +1556,7 @@ Apollo Portal需要在不同的环境访问不同的meta service(apollo-configse
1. 启动之后点击account-service服务配置后会提示环境缺失此时需要补全上边新增生产环境的配置
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0049.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0049.png" />
3. 点击左下方的补缺环境
@@ -1564,7 +1564,7 @@ Apollo Portal需要在不同的环境访问不同的meta service(apollo-configse
4. 补缺过生产环境后切换到PRO环境后会提示有Namespace缺失点击补缺
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0050.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0050.png" />
5. 从dev环境同步配置到pro
@@ -1610,7 +1610,7 @@ apollo-quickstart项目有两个客户端
1. 172.16.0.160
2. 172.16.0.170
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0051.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0051.png"/>
**灰度目标**
@@ -1626,7 +1626,7 @@ apollo-quickstart项目有两个客户端
2. 点击确定后,灰度版本就创建成功了,页面会自动切换到`灰度版本`Tab
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0052.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0052.png"/>
#### 灰度配置
@@ -1634,17 +1634,17 @@ apollo-quickstart项目有两个客户端
2. 在弹出框中填入要灰度的值3000点击提交
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0053.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0053.png"/>
#### 配置灰度规则
1. 切换到`灰度规则`Tab点击`新增规则`按钮
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0054.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0054.png" />
2. 在弹出框中`灰度的IP`下拉框会默认展示当前使用配置的机器列表选择我们要灰度的IP点击完成
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0055.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0055.png"/>
如果下拉框中没找到需要的IP说明机器还没从Apollo取过配置可以点击手动输入IP来输入输入完后点击添加按钮
@@ -1674,11 +1674,11 @@ vm options: `-Dapp.id=apollo-quickstart -Denv=DEV -Ddev_meta=http://localhost:80
}
```
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0056.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0056.png"/>
2. 切换到`配置`Tab再次检查灰度的配置部分如果没有问题点击`灰度发布`
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0057.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0057.png" />
3. 在弹出框中可以看到主版本的值是2000灰度版本即将发布的值是3000。填入其它信息后点击发布
@@ -1686,9 +1686,9 @@ vm options: `-Dapp.id=apollo-quickstart -Denv=DEV -Ddev_meta=http://localhost:80
4. 发布后,切换到`灰度实例列表`Tab就能看到172.16.0.160已经使用了灰度发布的值
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0058.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0058.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.9/Apollo/Simple_Introduction/0059.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/Apollo/Simple_Introduction/0059.png" />

View File

@@ -185,7 +185,7 @@ https://blog.csdn.net/iispring/article/details/51615631
## 区别+应用场景
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0001.png" width=90%>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0001.png" width=90%>
**总结:**
@@ -300,7 +300,7 @@ TCP通过三次握手建立可靠连接
在采用快恢复算法时慢开始算法只是在TCP连接建立时和网络出现超时时才使用。
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0002.png" width=80%>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0002.png" width=80%>

View File

@@ -30,15 +30,15 @@ date: 2021-04-03 14:21:58
网络网络Network由若干**结点Node**和连接这些结点的**链路Link**组成。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0001.png"/>
互联网或互连网多个网络通过路由器互连起来这样就构成了一个覆盖范围更大的网络即互连网互联网。因此互联网又称为“网络的网络Network of Networks”。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0002.png"/>
因特网因特网Internet是世界上最大的互连网络用户数以亿计互连的网络数以百万计
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0003.png"/>
@@ -52,13 +52,13 @@ date: 2021-04-03 14:21:58
### 因特网发展的三个阶段
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0004.png"/>
**因特网服务提供者`ISP`(`I`nternet `S`ervice `P`rovider)**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0005.png"/>
> Q普通用户是如何接入到因特网的呢
>
@@ -71,13 +71,13 @@ date: 2021-04-03 14:21:58
**中国的三大`ISP`:中国电信,中国联通,中国移动**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0006.png"/>
**基于ISP的三层结构的因特网**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0007.png">
1、图中红线部分是两个私人用户进行通信的大致链路可以看到需要经过多个ISP层级
@@ -100,7 +100,7 @@ date: 2021-04-03 14:21:58
* 因特网工程部IETF负责研究中短期工程问题主要针对协议的开发和标准化
* 因特网研究部IRTF从事理论方面的研究和开发一些需要长期考虑的问题。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0008.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0008.png" />
* 制订因特网的正式标准要经过一下**4个阶段**
@@ -124,7 +124,7 @@ date: 2021-04-03 14:21:58
由**大量网络**和连接这些网络的**路由器**组成。这部分是**为边缘部分提供服务**的(提供连通性和交换)。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0009.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0009.png" />
1. 路由器是一种专用计算机,但我们不称它为主机,路由器是实现分组交换的关键构建,其任务是转发收到的分组,这是网络核心最重要的部分。
@@ -142,12 +142,12 @@ date: 2021-04-03 14:21:58
### 电路交换
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0010.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0010.png" />
1. 传统两两相连的方式,当电话数量很多时,电话线也很多,就很不方便
2. 所以要使得每一部电话能够很方便地和另一部电话进行通信,就应该使用一个**中间设备**将这些电话连接起来,这个中间设备就是**电话交换机**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0011.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0011.png" />
* 电话交换机接通电话线的方式称为电路交换;
@@ -161,7 +161,7 @@ date: 2021-04-03 14:21:58
3、释放连接归还通信资源
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0012.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0012.png" />
1. 当使用电路交换来传送计算机数据时,其线路的传输效率往往很低。这是因为计算机数据是突发式地出现在传输线路上的,而不是像打电话一样一直占用着通信资源。
2. 试想一下这种情况,当用户正在输入和编辑一份待传输的文件时,用户所占用的通信资源暂时未被利用,该通信资源也不能被其它用户利用,宝贵的通信线路资源白白被浪费了
@@ -169,7 +169,7 @@ date: 2021-04-03 14:21:58
### 分组交换
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0013.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0013.png"/>
1. 通常我们把表示该消息的整块数据成为一个**报文**。
2. 在发送报文之前,先把较长的报文划分成一个个更小的等长数据段,在每一个数据段前面。加上一些由必要的控制信息组成的首部后,就构成一个分组,也可简称为“包”,相应地,首部也可称为“包头”。首部起到了很大的作用,首先首部中肯定包含了分组的目的地址,否则分组传输路径中的各分组交换机(也就是个路由器)就不知道如何转发分组了
@@ -212,7 +212,7 @@ date: 2021-04-03 14:21:58
假设ABCD是分组传输路径所要经过的4个结点交换机纵坐标为时间
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0014.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0014.png"/>
@@ -232,7 +232,7 @@ date: 2021-04-03 14:21:58
* 可以随时发送分组,而不需要事先建立连接。构成原始报文的**一个个分组****依次**在各结点交换机上存储转发。各结点交换机在发送分组的同时,还缓存接收到的分组。
* **构成原始报文的一个个分组**,在各结点交换机上进行存储转发,相比报文交换,减少了转发时延,还可以避免过长的报文长时间占用链路,同时也有利于进行差错控制。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0015.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0015.png"/>
@@ -249,7 +249,7 @@ date: 2021-04-03 14:21:58
* 计算机网络所连接的硬件,并不限于一般的计算机,而是包括了智能手机等智能硬件。
* 计算机网络并非专门用来传送数据,而是能够支持很多种的应用(包括今后可能出现的各种应用)。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0016.png">
上图所示的各终端机只是具有显示和输入设备的终端,而并不是自治的计算机,所以上图并不是计算机网络,只是一个运行分时系统的大型机系统。
@@ -293,7 +293,7 @@ date: 2021-04-03 14:21:58
* 总线型网络
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0017.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0017.png" />
优点:建网容易,增减节点方便,节省线路
@@ -301,7 +301,7 @@ date: 2021-04-03 14:21:58
* 星型网络
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0018.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0018.png" />
星型网络图中的中央设备现在一般是交换机或路由器,
@@ -313,13 +313,13 @@ date: 2021-04-03 14:21:58
* 环形网络
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0019.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0019.png" />
环中信号是单向传输的
* 网状型网络
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0020.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0020.png" />
一般情况下,每个节点至少由两条路径与其他节点相连,多用在广域网中
@@ -333,15 +333,15 @@ date: 2021-04-03 14:21:58
### 速率
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0021.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0021.png"/>
### 带宽
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0022.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0022.png"/>
### 吞吐量
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0023.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0023.png" />
带宽1 Gb/s的以太网代表其额定速率是1 Gb/s这个数值也是该以太网的吞吐量的上限值。实际上对于带宽1 Gb/s的以太网可能实际吞吐量只有 700 Mb/s甚至更低。
@@ -361,33 +361,33 @@ date: 2021-04-03 14:21:58
网卡的发送速率,信道带宽,交换机的接口速率,它们共同决定着主机的发送速率。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0024.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0024.png"/>
当处理时延忽略不计时,发送时延和传播时延谁占主导,要具体情况具体分析
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0025.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0025.png"/>
### 时延带宽积
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0026.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0026.png"/>
### 往返时间
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0027.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0027.png"/>
### 利用率
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0028.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0028.png"/>
### 丢包率
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0029.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0029.png"/>
@@ -397,7 +397,7 @@ date: 2021-04-03 14:21:58
### 常见的计算机网络体系结构
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0030.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0030.png"/>
1、如今用的最多的是TCP/IP体系结构现今规模最大的、覆盖全球的、基于TCP/IP的互联网并未使用OSI标准。TCP/IP体系结构相当于将OSI体系结构的物理层和数据链路层合并为了网络接口层并去掉了会话层和表示层。
@@ -405,7 +405,7 @@ date: 2021-04-03 14:21:58
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0031.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0031.png"/>
1、在用户主机的操作系统中通常都带有符合TCP/IP体系结构标准的TCP/IP协议族。而用于网络互连的路由器中也带有符合TCP/IP体系结构标准的TCP/IP协议族。只不过路由器一般只包含网络接口层和网际层。
@@ -435,7 +435,7 @@ date: 2021-04-03 14:21:58
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0032.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0032.png"/>
@@ -443,11 +443,11 @@ date: 2021-04-03 14:21:58
### 分层的必要性
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0033.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0033.png" />
**物理层问题**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0034.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0034.png" />
@@ -455,7 +455,7 @@ date: 2021-04-03 14:21:58
**数据链路层问题**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0035.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0035.png" />
1、如图所示主机A要给主机C发送数据。
@@ -479,7 +479,7 @@ A也就是需要解决分组的封装格式问题
Q另外对于总线型的网络还会出现下面这种典型的问题
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0036.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0036.png" />
@@ -493,7 +493,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0037.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0037.png" />
@@ -503,13 +503,13 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
- 引出了我们如何标识与网络通信相关的引用进程,进而解决进程之间基于网络通信的问题。
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0038.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0038.png"/>
**应用层问题**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0039.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0039.png"/>
@@ -517,7 +517,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0040.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0040.png"/>
### 分层思想举例
@@ -525,7 +525,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
假设网络拓扑如下所示
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0041.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0041.png" />
@@ -533,7 +533,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
主机和Web服务器之间基于网络的通信实际上是主机中的浏览器应用进程与Web服务器中的Web服务器应用进程之间基于网络的通信
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0042.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0042.png"/>
@@ -545,7 +545,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
- 应用层将HTTP请求报文交付给运输层处理
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0043.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0043.png"/>
@@ -559,7 +559,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
- 运输层将TCP报文段交付给网络层处理
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0044.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0044.png"/>
@@ -571,7 +571,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
- 网络层将IP数据报交付给数据链路层处理
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0045.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0045.png"/>
@@ -589,7 +589,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0046.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0046.png"/>
@@ -599,7 +599,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
* 前导码的作用是为了让目的主机做好接收帧的准备
* 物理层将装有前导码的比特流变换成相应的信号发送给传输媒体
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0047.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0047.png" />
@@ -607,7 +607,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
- 信号通过传输媒体到达路由器
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0048.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0048.png" />
@@ -646,7 +646,7 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
**发回响应报文的步骤和之前过程类似**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0049.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0049.png"/>
### 专用术语
@@ -654,13 +654,13 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
**实体**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0050.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0050.png" />
**协议**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0051.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0051.png"/>
1、协议控制两个对等实体进行逻辑通信的规则的集合
@@ -676,13 +676,13 @@ Q另外对于总线型的网络还会出现下面这种典型的问题
**服务**
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0052.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0052.png" />
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0053.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0053.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.9/computer_network/gaishu/0054.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.9/computer_network/gaishu/0054.png"/>

View File

@@ -46,7 +46,7 @@ date: 2020-02-03 13:11:45
下图是ElasticSearch的索引结构下边黑色部分是物理结构上边黄色部分是逻辑结构逻辑结构也是为了更好的 去描述ElasticSearch的工作原理及去使用物理结构中的索引文件。
<img src="https://img.imlql.cn/youthlql@1.0.8/ElasticSearch/Introduction/0001.png" width=80%>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/ElasticSearch/Introduction/0001.png" width=80%>
逻辑结构部分是一个倒排索引表:
@@ -58,11 +58,11 @@ date: 2020-02-03 13:11:45
如下:
<img src="https://img.imlql.cn/youthlql@1.0.8/ElasticSearch/Introduction/0002.png" width=40%>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/ElasticSearch/Introduction/0002.png" width=40%>
现在,如果我们想搜到`quick brown`我们只需要查找包含每个词条的文档:
<img src="https://img.imlql.cn/youthlql@1.0.8/ElasticSearch/Introduction/0003.png" width=80%>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/ElasticSearch/Introduction/0003.png" width=80%>
两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单 相似性算法 那么,我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳
@@ -236,7 +236,7 @@ http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000
使用postman测试
<img src="https://img.imlql.cn/youthlql@1.0.8/ElasticSearch/Introduction/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/ElasticSearch/Introduction/0004.png">

View File

@@ -20,7 +20,7 @@ date: 2020-11-16 18:14:02
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0001.png">
1. Java 和 C++语言的区别就在于垃圾收集技术和内存动态分配上C++语言没有垃圾收集技术,需要程序员手动的收集。
@@ -102,7 +102,7 @@ date: 2020-11-16 18:14:02
**十几年前磁盘碎片整理的日子**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0002.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0002.jpg">
@@ -174,7 +174,7 @@ date: 2020-11-16 18:14:02
### 应该关心哪些区域的回收?
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0003.png">
1. 垃圾收集器可以对年轻代回收,也可以对老年代回收,甚至是全栈和方法区的回收,
1. 其中,**Java堆是垃圾收集器的工作重点**
@@ -216,7 +216,7 @@ date: 2020-11-16 18:14:02
### 循环引用
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0004.png">
当p的指针断开的时候内部的引用形成一个循环计数器都还算1无法被回收这就是循环引用从而造成内存泄漏
@@ -255,7 +255,7 @@ public class RefCountGC {
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0005.png">
* 如果不小心直接把`obj1.reference`和`obj2.reference`置为null。则在Java堆中的两块内存依然保持着互相引用无法被回收
@@ -353,7 +353,7 @@ Process finished with exit code 0
3. 如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。
4. 在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0006.png">
@@ -371,7 +371,7 @@ Process finished with exit code 0
- 基本数据类型对应的Class对象一些常驻的异常对象NullPointerException、OutofMemoryError系统类加载器。
7. 反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0007.png">
@@ -453,7 +453,7 @@ Object 类中 finalize() 源码
**通过 JVisual VM 查看 Finalizer 线程**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0008.png">
@@ -579,7 +579,7 @@ MAT与JProfiler的GC Roots溯源
**方式一:命令行使用 jmap**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0009.png">
@@ -631,23 +631,23 @@ public class GCRootsTest {
1、先执行第一步然后停下来去生成此步骤dump文件
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0010.png">
2、 点击【堆 Dump】
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0011.png">
3、右键 --\> 另存为即可
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0012.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0012.jpg">
4、输入命令继续执行程序
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0013.png">
5、我们接着捕获第二张堆内存快照
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0014.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0014.jpg">
@@ -657,19 +657,19 @@ public class GCRootsTest {
> 点击Open Heap Dump也行
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0015.png">
2、选择Java Basics --> GC Roots
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0016.png">
3、第一次捕捉堆内存快照时GC Roots 中包含我们定义的两个局部变量,类型分别为 ArrayList 和 DateTotal:21
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0017.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0017.jpg">
4、打开第二个dump文件第二次捕获内存快照时由于两个局部变量引用的对象被释放所以这两个局部变量不再作为 GC Roots ,从 Total Entries = 19 也可以看出(少了两个 GC Roots
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0018.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0018.jpg">
@@ -716,13 +716,13 @@ public class GCRootsTest {
1、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0019.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0019.jpg">
2、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0020.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0021.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0021.jpg">
可以发现颜色变绿了,可以动态的看变化
@@ -730,11 +730,11 @@ public class GCRootsTest {
3、右击对象选择 Show Selection In Heap Walker单独的查看某个对象
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0022.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0023.png">
@@ -742,13 +742,13 @@ public class GCRootsTest {
点击Show Paths To GC Roots在弹出界面中选择默认设置即可
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0024.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0024.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0025.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0025.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0026.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0026.png">
### JProfiler 分析 OOM
@@ -798,11 +798,11 @@ count = 6
1、看这个超大对象
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0027.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0027.png">
2、揪出 main() 线程中出问题的代码
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0028.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0028.png">
@@ -837,7 +837,7 @@ count = 6
* 注意:标记的是被引用的对象,也就是可达对象,并非标记的是即将被清除的垃圾对象
2. 清除Collector对堆内存从头到尾进行线性的遍历如果发现某个对象在其Header中没有标记为可达对象则将其回收
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0029.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0029.png">
@@ -876,7 +876,7 @@ count = 6
将活着的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0030.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0030.png">
新生代里面就用到了复制算法Eden区和S0区存活对象整体复制到S1区
@@ -906,7 +906,7 @@ count = 6
2. 老年代大量的对象存活,那么复制的对象将会有很多,效率会很低
3. 在新生代对常规应用的垃圾回收一次通常可以回收70% - 99% 的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0031.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0031.png">
@@ -935,7 +935,7 @@ count = 6
2. 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0032.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0032.png">
@@ -1066,7 +1066,7 @@ A没有最好的算法只有最合适的算法
1. 一般来说在相同条件下堆空间越大一次GC时所需要的时间就越长有关GC产生的停顿也越长。为了更好地控制GC产生的停顿时间将一块大的内存区域分割成多个小块根据目标的停顿时间每次合理地回收若干个小区间而不是整个堆空间从而减少一次GC所产生的停顿。
3. 分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间。每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_010/0033.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_010/0033.png">

View File

@@ -117,7 +117,7 @@ JVM参数
2、我也查过了大对象阈值的默认值
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0001.png">
我不太懂这个默认值为啥是0我猜测可能是代表什么比例目前也没有搜到相关的东西。这个不太重要暂时就没有太深究希望读者有知道的可以告知我一声。
@@ -185,11 +185,11 @@ Heap
1、来看看字节码实例方法局部变量表第一个变量肯定是 this
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0002.png">
2、你有没有看到局部变量表的大小是 2。但是局部变量表里只有一个索引为0的啊那索引为1的是哪个局部变量呢实际上索引为1的位置是buffer在占用着执行 System.gc() 时,栈中还有 buffer 变量指向堆中的字节数组所以没有进行GC
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0003.png">
3、那么这种代码块的情况什么时候会被GC呢我们来看第四个方法
@@ -217,11 +217,11 @@ A局部变量表长度为 2 这说明了出了代码块时buffer 就出
> 这点看不懂的可以看我前面的文章:虚拟机栈 --> Slot的重复利用
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0004.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0005.png">
@@ -302,7 +302,7 @@ Heap
右边的图后期有一些对象不用了按道理应该断开引用但是存在一些链没有断开图示中的Forgotten Reference Memory Leak从而导致没有办法被回收。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0006.png">
@@ -446,7 +446,7 @@ Process finished with exit code -1
2. 并发不是真正意义上的“同时进行”只是CPU把一个时间段划分成几个时间片段时间区间然后在这几个时间区间之间来回切换。由于CPU处理的速度非常快只要时间间隔处理得当即可让用户感觉是多个应用程序同时在进行
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0007.png">
@@ -461,7 +461,7 @@ Process finished with exit code -1
3. 适合科学计算,后台处理等弱交互场景
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0008.png">
> **并发与并行的对比**
@@ -482,7 +482,7 @@ Process finished with exit code -1
* 相较于并行的概念,单线程执行。
* 如果内存不够则程序暂停启动JVM垃圾回收器进行垃圾回收单线程
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0009.png">
@@ -492,7 +492,7 @@ Process finished with exit code -1
- 比如用户程序在继续运行而垃圾收集程序线程运行于另一个CPU上
2. 典型垃圾回收器CMS、G1
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0010.png">
@@ -551,7 +551,7 @@ Process finished with exit code -1
1、一般的垃圾回收算法至少会划分出两个年代年轻代和老年代。但是单纯的分代理论在垃圾回收的时候存在一个巨大的缺陷为了找到年轻代中的存活对象却不得不遍历整个老年代反过来也是一样的。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0011.png">
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种引用类型对应的类开发人员可以在应用程序中直接使用它们。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0012.png" >
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0012.png" >
@@ -661,7 +661,7 @@ Hello,尚硅谷
`StringBuffer str = new StringBuffer("hello,尚硅谷");`
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_011/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_011/0013.png">

View File

@@ -44,7 +44,7 @@ GC 分类与性能指标
**按线程数分(垃圾回收线程数),可以分为串行垃圾回收器和并行垃圾回收器。**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0001.png">
1. 串行回收指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作此时工作线程被暂停直至垃圾收集工作结束。
1. 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合串行回收器的性能表现可以超过并行回收器和并发回收器。所以串行回收默认被应用在客户端的Client模式下的JVM中
@@ -60,7 +60,7 @@ GC 分类与性能指标
1. 并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。
2. 独占式垃圾回收器Stop the World一旦运行就停止应用程序中的所有用户线程直到垃圾回收过程完全结束。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0002.png">
@@ -105,7 +105,7 @@ GC 分类与性能指标
2. 这种情况下,应用程序能容忍较高的暂停时间,因此,高吞吐量的应用程序有更长的时间基准,快速响应是不必考虑的
3. 吞吐量优先意味着在单位时间内STW的时间最短0.2+0.2=0.4
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0003.png">
@@ -115,7 +115,7 @@ GC 分类与性能指标
- 例如GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的
2. 暂停时间优先意味着尽可能让单次STW的时间最短0.1+0.1 + 0.1+ 0.1+ 0.1=0.5但是总的GC时间可能会长
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0004.png">
@@ -169,17 +169,17 @@ GC 分类与性能指标
2. 并行回收器ParNew、Parallel Scavenge、Parallel old
3. 并发回收器CMS、G1
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0005.png">
**官方文档**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0006.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0006.jpg">
**7款经典回收器与垃圾分代之间的关系**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0007.png">
1. 新生代收集器Serial、ParNew、Parallel Scavenge
@@ -192,7 +192,7 @@ GC 分类与性能指标
### 垃圾收集器的组合关系
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0008.png">
@@ -251,11 +251,11 @@ jinfo -flag UseParallelOldGC 进程id
JDK 8 中默认使用 ParallelGC 和 ParallelOldGC 的组合
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0009.png">
#### JDK9
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0010.png">
@@ -281,7 +281,7 @@ Serial 回收器:串行回收
这个收集器是一个单线程的收集器“单线程”的意义它只会使用一个CPU串行或一条收集线程去完成垃圾收集工作。更重要的是在它进行垃圾收集时必须暂停其他所有的工作线程直到它收集结束Stop The World
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0011.png">
@@ -313,7 +313,7 @@ ParNew 回收器:并行回收
2. ParNew 收集器除了采用**并行回收**的方式执行内存回收外两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。
3. ParNew 是很多JVM运行在Server模式下新生代的默认垃圾收集器。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0012.png">
1. 对于新生代,回收次数频繁,使用并行方式高效。
2. 对于老年代回收次数少使用串行方式节省资源。CPU并行需要切换线程串行可以省去切换线程的资源
@@ -361,7 +361,7 @@ Parallel 回收器:吞吐量优先
5. Parallel Old收集器采用了标记-压缩算法,但同样也是基于并行回收和"Stop-the-World"机制。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0013.png">
1. 在程序吞吐量优先的应用场景中Parallel收集器和Parallel Old收集器的组合在server模式下的内存回收性能很不错。
2. **在Java8中默认是此垃圾收集器。**
@@ -418,7 +418,7 @@ CMS 回收器:低延迟
### CMS 工作原理(过程)
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0014.png">
CMS整个过程比之前的收集器要复杂整个过程分为4个主要阶段即初始标记阶段、并发标记阶段、重新标记阶段和并发清除阶段。(涉及STW的阶段主要是初始标记 和 重新标记)
@@ -438,7 +438,7 @@ CMS整个过程比之前的收集器要复杂整个过程分为4个主要阶
3. 另外由于在垃圾收集阶段用户线程没有中断所以在CMS回收过程中还应该确保应用程序用户线程有足够的内存可用。因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集**而是当堆内存使用率达到某一阈值时,便开始进行回收**以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要就会出现一次**“Concurrent Mode Failure”** 失败这时虚拟机将启动后备预案临时启用Serial old收集器来重新进行老年代的垃圾收集这样停顿时间就很长了。
4. CMS收集器的垃圾收集算法采用的是**标记清除算法**,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,**不可避免地将会产生一些内存碎片**。那么CMS在为新对象分配内存空间时将无法使用指针碰撞Bump the Pointer技术而只能够选择空闲列表Free List执行内存分配。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0015.png">
@@ -558,11 +558,11 @@ G1 回收器:区域化分代式
G1的分代已经不是下面这样的了
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0016.png">
G1的分区是这样的一个区域
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0017.png">
**空间整合**
@@ -655,7 +655,7 @@ G1中提供了三种垃圾回收模式YoungGC、Mixed GC和Full GC在不
>
> 如图所示可以将区域分配到Eden幸存者和旧时代区域。 此外,还有第四种类型的物体被称为巨大区域。 这些区域旨在容纳标准区域大小的50或更大的对象。 它们存储为一组连续区域。 最后,最后一种区域类型是堆的未使用区域。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0018.png">
@@ -667,7 +667,7 @@ G1中提供了三种垃圾回收模式YoungGC、Mixed GC和Full GC在不
**Regio的细节**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0019.png">
1. 每个Region都是通过指针碰撞来分配空间
2. G1为每一个Region设 计了两个名为TAMSTop at Mark Start的指针把Region中的一部分空间划分出来用于并发回收过程中的新对象分配并发回收时新分配的对象地址都必须要在这两个指针位置以上。
@@ -686,7 +686,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节
* 混合回收Mixed GC
* 如果需要单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制即强力回收。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0020.png">
顺时针Young GC --> Young GC+Concurrent Marking --> Mixed GC顺序进行垃圾回收
@@ -731,7 +731,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0021.png">
1. 在回收 Region 时,为了不进行全堆的扫描,引入了 Remembered Set
2. Remembered Set 记录了当前 Region 中的对象被哪个对象引用了
@@ -748,7 +748,7 @@ G1 GC的垃圾回收过程主要包括如下三个环节
2. 年轻代回收只回收Eden区和Survivor区
3. YGC时首先G1停止应用程序的执行Stop-The-WorldG1创建回收集Collection Set回收集是指需要被回收的内存分段的集合年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0022.png">
<img src="https://npm.elemecdn.com/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。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0023.png">
@@ -854,11 +854,11 @@ G1 GC的垃圾回收过程主要包括如下三个环节
截止JDK1.8一共有7款不同的垃圾收集器。每一款的垃圾收集器都有不同的特点在具体使用的时候需要根据具体的情况选用不同的垃圾收集器。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0034.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0034.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0024.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0024.png">
@@ -922,11 +922,11 @@ GC 日志分析
2、这个只会显示总的GC堆的变化如下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0025.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0025.png">
3、参数解析
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0026.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0026.png">
@@ -938,11 +938,11 @@ GC 日志分析
2、输入信息如下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0027.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0027.png">
3、参数解析
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0028.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0028.png">
@@ -954,7 +954,7 @@ GC 日志分析
2、输出信息如下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0029.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0029.png">
3、说明日志带上了日期和时间
@@ -989,13 +989,13 @@ GC 日志分析
#### Young GC
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0030.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0030.png">
#### Full GC
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0031.png">
<img src="https://npm.elemecdn.com/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 区
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0032.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0032.png">
2、然后我们将4M对象存入到Eden区中
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0033.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0033.png">
老年代图画的有问题free应该是4M
@@ -1056,7 +1056,7 @@ Process finished with exit code 0
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0035.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0035.jpg">
与 JDK7 不同的是JDK8 直接判定 4M 的数组为大对象,直接怼到老年区去了
@@ -1082,15 +1082,15 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
在线分析网址gceasy.io
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0036.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0036.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0037.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0037.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0038.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0038.png">
@@ -1126,7 +1126,7 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
1. 停顿时间比其他几款收集器确实有了质的飞跃,但也未实现最大停顿时间控制在十毫秒以内的目标。
2. 而吞吐量方面出现了明显的下降,总运行时间是所有测试收集器里最长的。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0039.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0039.png">
@@ -1156,7 +1156,7 @@ GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
**吞吐量**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0040.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0040.png">
max-JOPS以低延迟为首要前提下的数据
@@ -1166,13 +1166,13 @@ critical-JOPS不考虑低延迟下的数据
**低延迟**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0041.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0041.png">
在ZGC的强项停顿时间测试上它毫不留情的将Parallel、G1拉开了两个数量级的差距。无论平均停顿、95%停顿、998停顿、99. 98停顿还是最大停顿时间ZGC都能毫不费劲控制在10毫秒以内。
虽然ZGC还在试验状态没有完成所有特性但此时性能已经相当亮眼用“令人震惊、革命性”来形容不为过。未来将在服务端、大内存、低延迟应用的首选垃圾收集器。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0042.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0042.png">
@@ -1192,4 +1192,4 @@ critical-JOPS不考虑低延迟下的数据
AliGC是阿里巴巴JVM团队基于G1算法面向大堆LargeHeap应用场景。指定场景下的对比
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_012/0043.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_012/0043.png">

View File

@@ -38,7 +38,7 @@ date: 2020-11-02 11:51:56
3. 新项目上线对各种JVM参数设置一脸茫然直接默认吧然后就JJ了。
4. 每次面试之前都要重新背一遍JVM的一些原理概念性的东西然而面试官却经常问你在实际项目中如何调优VM参数如何解决GC、OOM等问题一脸懵逼。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0001.png">
大部分Java开发人员除了会在项目中使用到与Java平台相关的各种高精尖技术对于Java技术的核心Java虚拟机了解甚少。
@@ -50,7 +50,7 @@ date: 2020-11-02 11:51:56
1. 一些有一定工作经验的开发人员打心眼儿里觉得SSM、微服务等上层技术才是重点基础技术并不重要这其实是一种本末倒置的“病态”。
2. 如果我们把核心类库的API比做数学公式的话那么Java虚拟机的知识就好比公式的推导过程。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0002.png">
- 计算机系统体系对我们来说越来越远,在不了解底层实现方式的前提下,通过高级语言很容易编写程序代码。但事实上计算机并不认识高级语言。
@@ -87,7 +87,7 @@ Java VS C++
1. 垃圾收集机制为我们打理了很多繁琐的工作大大提高了开发的效率但是垃圾收集也不是万能的懂得JVM内部的内存结构、工作机制是设计高扩展性应用和诊断运行时问题的基础也是Java工程师进阶的必备能力。
2. C++语言需要程序员自己来分配内存和回收内存对于高手来说可能更加舒服但是对于普通开发者如果技术实力不够很容易造成内存泄漏。而Java全部交给JVM进行内存分配和回收这也是一种趋势减少程序员的工作量。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0003.png">
## 什么人需要学JVM
@@ -103,26 +103,26 @@ Java VS C++
**英文文档规范**https://docs.oracle.com/javase/specs/index.html
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0004.png">
**中文书籍:**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0005.png">
> 周志明老师的这本书**非常推荐看**,不过只推荐看第三版,第三版较第二版更新了很多,个人觉得没必要再看第二版。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0006.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0007.png">
TIOBE排行榜
-----------
**TIOBE 排行榜**https://www.tiobe.com/tiobe-index/
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0008.png">
- 世界上没有最好的编程语言,只有最适用于具体应用场景的编程语言。
- 目前网上一直流传Java被pythongo撼动Java第一的地位。学习者不需要太担心Java强大的生态圈也不是说是朝夕之间可以被撼动的。
@@ -148,14 +148,14 @@ Java-跨平台的语言
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0009.png">
JVM-跨语言的平台
------
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0010.png">
@@ -187,7 +187,7 @@ JVM-跨语言的平台
2. 自己动手写一个Java虚拟机难吗
3. 天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0011.png">
Java发展重大事件
------------
@@ -215,7 +215,7 @@ Java发展重大事件
## Open JDK和Oracle JDK
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0012.png">
- 在JDK11之前Oracle JDK中还会存在一些Open JDK中没有的闭源的功能。但在JDK11中我们可以认为Open JDK和Oracle JDK代码实质上已经达到完全一致的程度了。
- 主要的区别就是两者更新周期不一样
@@ -258,11 +258,11 @@ JVM的位置
JVM是运行在操作系统之上的它与硬件没有直接的交互
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0013.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0014.png">
@@ -275,7 +275,7 @@ JVM的整体结构
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0015.png">
@@ -284,7 +284,7 @@ Java代码执行流程
凡是能生成被Java虚拟机所能解释、运行的字节码文件那么理论上我们就可以自己设计一套语言了
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_001/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_001/0016.png">

View File

@@ -25,19 +25,19 @@ date: 2020-11-02 21:31:58
### 简图
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0001.png">
### 详细图
英文版
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0002.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0002.jpg">
中文版
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0003.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0003.jpg">
注意方法区只有HotSpot虚拟机有J9JRockit都没有
@@ -59,7 +59,7 @@ date: 2020-11-02 21:31:58
3. **加载的类信息存放于一块称为方法区的内存空间**。除了类的信息外方法区中还会存放运行时常量池信息可能还包括字符串字面量和数字常量这部分常量信息是Class文件中常量池部分的内存映射
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0004.png">
@@ -71,7 +71,7 @@ date: 2020-11-02 21:31:58
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0005.png">
类加载过程
-------
@@ -96,11 +96,11 @@ public class HelloLoader {
* 加载成功,则进行链接、初始化等操作。完成后调用 HelloLoader 类中的静态方法 main
* 加载失败则抛出异常
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0006.png">
完整的流程图如下所示:
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0007.png">
@@ -142,7 +142,7 @@ public class HelloLoader {
使用 BinaryViewer软件查看字节码文件其开头均为 CAFE BABE ,如果出现不合法的字节码文件,那么将会验证不通过。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0008.png">
#### 准备(Prepare)
@@ -185,7 +185,7 @@ public class HelloApp {
* 反编译 class 文件后可以查看符号引用,下面带# 的就是符号引用
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0023.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0023.png"/>
### 初始化阶段
@@ -225,7 +225,7 @@ public class HelloApp {
查看下面这个代码的字节码,可以发现有一个`<clinit>()`方法。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0009.png">
```java
public class ClassInitTest {
@@ -277,15 +277,15 @@ public class ClassInitTest {
**举例2无 static 变量**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0010.png">
加上之后就有了
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0011.png">
#### 4说明
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0012.png">
在构造器中:
@@ -296,7 +296,7 @@ public class ClassInitTest {
若该类具有父类JVM会保证子类的`<clinit>()`执行前,父类的`<clinit>()`已经执行完毕
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0013.png">
如上代码,加载流程如下:
@@ -374,17 +374,17 @@ class DeadThread{
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0014.png">
**ExtClassLoader**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0015.png">
**AppClassLoader**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0016.png">
@@ -579,15 +579,15 @@ public class CustomClassLoader extends ClassLoader {
ClassLoader类它是一个抽象类其后所有的类加载器都继承自ClassLoader不包括启动类加载器
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0017.png">
sun.misc.Launcher 它是一个java虚拟机的入口应用
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0018.png">
#### 获取ClassLoader途径
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0019.png">
@@ -640,7 +640,7 @@ Java虚拟机对class文件采用的是**按需加载**的方式,也就是说
3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0020.png">
### 双亲委派机制代码演示
@@ -707,7 +707,7 @@ public class String {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0021.png">
由于双亲委派机制一直找父类所以最后找到了Bootstrap ClassLoaderBootstrap ClassLoader找到的是 JDK 自带的 String 类在那个String类中并没有 main() 方法,所以就报了上面的错误。
@@ -765,7 +765,7 @@ Process finished with exit code 1
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_002/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_002/0022.png">

View File

@@ -27,15 +27,15 @@ date: 2020-11-09 15:38:42
本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0001.png">
当我们通过前面的:类的加载 --> 验证 --> 准备 --> 解析 --\> 初始化,这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0002.png">
类比一下也就是大厨做饭,我们把大厨后面的东西(切好的菜,刀,调料),比作是运行时数据区。而厨师可以类比于执行引擎,将通过准备的东西进行制作成精美的菜品。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0003.png">
@@ -50,7 +50,7 @@ date: 2020-11-09 15:38:42
> 下图来自阿里巴巴手册JDK8
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0004.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0004.jpg">
@@ -62,7 +62,7 @@ date: 2020-11-09 15:38:42
- 线程独有:独立包括程序计数器、栈、本地方法栈
- 线程间共享:堆、堆外内存(永久代或元空间、代码缓存)
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0005.png">
@@ -70,7 +70,7 @@ date: 2020-11-09 15:38:42
**每个JVM只有一个Runtime实例**。即为运行时环境,相当于内存结构的中间的那个框框:运行时环境。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0006.png">
<img src="https://npm.elemecdn.com/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
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0007.png">
1. JVM中的程序计数寄存器Program Counter RegisterRegister的命名源于CPU的寄存器**寄存器存储指令相关的现场信息**。CPU只有把数据装载到寄存器才能够运行。
2. 这里并非是广义上所指的物理寄存器或许将其翻译为PC计数器或指令计数器会更加贴切也称为程序钩子并且也不容易引起一些不必要的误会。**JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟**。
@@ -132,7 +132,7 @@ PC寄存器介绍
PC寄存器用来存储指向下一条指令的地址也即将要执行的指令代码。由执行引擎读取下一条指令并执行该指令。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0008.png">
@@ -268,7 +268,7 @@ SourceFile: "PCRegisterTest.java"
* 左边的数字代表**指令地址(指令偏移)**,即 PC 寄存器中可能存储的值,然后执行引擎读取 PC 寄存器中的值,并执行该指令
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0009.png">
@@ -282,7 +282,7 @@ SourceFile: "PCRegisterTest.java"
2. JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0010.png">
@@ -306,7 +306,7 @@ CPU 时间片
3. 但在微观上由于只有一个CPU一次只能处理程序要求的一部分如何处理公平一种方法就是引入时间片**每个程序轮流执行**。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0011.png">
@@ -314,7 +314,7 @@ CPU 时间片
## 本地方法
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0012.png">
@@ -393,7 +393,7 @@ Java使用起来非常方便然而有些层次的任务用Java实现起来不
4. 本地方法一般是使用C语言或C++语言实现的。
5. 它的具体做法是Native Method Stack中登记native方法在Execution Engine 执行时加载本地方法库。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_003/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_003/0013.png">

View File

@@ -31,7 +31,7 @@ date: 2020-11-10 10:38:42
1. 首先栈是运行时的单位,而堆是存储的单位。
2. 即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放哪里
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0001.png">
@@ -66,7 +66,7 @@ date: 2020-11-10 10:38:42
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0002.png">
- 虚拟机栈的生命周期
- 生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了
@@ -90,7 +90,7 @@ date: 2020-11-10 10:38:42
- 对于栈来说不存在垃圾回收问题
- 栈不需要GC但是可能存在OOM
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0003.png">
### 虚拟机栈的异常
@@ -164,7 +164,7 @@ Exception in thread "main" java.lang.StackOverflowError
**设置栈参数之后**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0004.png">
部分输出结果
@@ -202,7 +202,7 @@ Exception in thread "main" java.lang.StackOverflowError
4. 如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0005.png">
1. **不同线程中所包含的栈帧是不允许存在相互引用的**,即不可能在一个栈帧之中引用另外一个线程的栈帧。
2. 如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
@@ -230,11 +230,11 @@ Exception in thread "main" java.lang.StackOverflowError
- 一些附加信息
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0006.png">
并行每个线程下的栈都是私有的,因此每个线程都有自己各自的栈,并且每个栈里面都有很多栈帧,栈帧的大小主要由局部变量表 和 操作数栈决定的
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0007.png">
局部变量表
-------
@@ -312,7 +312,7 @@ public class LocalVariablesTest {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0008.png">
看完字节码后,可得结论:所以局部变量表所需的容量大小是在编译期确定下来的。
@@ -324,29 +324,29 @@ public class LocalVariablesTest {
1、0-15 也就是有16行字节码
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0009.png">
2、方法异常信息表
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0010.png">
3、Misc
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0011.png">
4、行号表
Java代码的行号和字节码指令行号的对应关系
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0012.png">
5、注意生效行数和剩余有效行数都是针对于字节码文件的行数
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0013.png">
1、图中圈的东西表示该局部变量的作用域
@@ -370,7 +370,7 @@ Java代码的行号和字节码指令行号的对应关系
6. 如果需要访问局部变量表中一个64bit的局部变量值时只需要使用前一个索引即可。比如访问long或double类型变量
7. 如果当前帧是由构造方法或者实例方法创建的,那么**该对象引用this将会存放在index为0的slot处**其余的参数按照参数表顺序继续排列。this也相当于一个变量
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0014.png">
### Slot代码示例
@@ -388,7 +388,7 @@ Java代码的行号和字节码指令行号的对应关系
局部变量表this 存放在 index = 0 的位置
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0015.png">
@@ -408,7 +408,7 @@ Java代码的行号和字节码指令行号的对应关系
weight 为 double 类型index 直接从 3 蹦到了 5
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0016.png">
@@ -451,7 +451,7 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
局部变量 c 重用了局部变量 b 的 slot 位置
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0017.png">
@@ -499,13 +499,13 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
- 比如:执行复制、交换、求和等操作
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0018.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0019.png">
@@ -536,7 +536,7 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0020.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0020.jpg">
局部变量表就相当于食材
@@ -571,23 +571,23 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
10 return
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0021.png">
### 一步一步看流程
1、首先执行第一条语句PC寄存器指向的是0也就是指令地址为0然后使用bipush让操作数15入操作数栈。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0022.png">
2、执行完后PC寄存器往下移指向下一行代码下一行代码就是将操作数栈的元素存储到局部变量表1的位置istore_1我们可以看到局部变量表的已经增加了一个元素。并且操作数栈为空了
* 解释为什么局部变量表索引从 1 开始,因为该方法为实例方法,局部变量表索引为 0 的位置存放的是 this
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0023.png">
3、然后PC下移指向的是下一行。让操作数8也入栈同时执行store操作存入局部变量表中
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0024.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0024.png">
@@ -595,11 +595,11 @@ this 不存在与 static 方法的局部变量表中,所以无法调用
iload_1取出局部变量表中索引为1的数据入操作数栈
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0025.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0025.png">
5、然后将操作数栈中的两个元素执行相加操作并存储在局部变量表3的位置
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0026.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0026.png">
@@ -607,7 +607,7 @@ iload_1取出局部变量表中索引为1的数据入操作数栈
**关于类型转换的说明**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0027.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0027.png">
@@ -616,7 +616,7 @@ iload_1取出局部变量表中索引为1的数据入操作数栈
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0028.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0028.png">
- m改成800之后byte存储不了就成了short型sipush 800
@@ -645,11 +645,11 @@ iload_1取出局部变量表中索引为1的数据入操作数栈
getSum() 方法字节码指令:最后带着个 ireturn
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0029.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0029.png">
testGetSum() 方法字节码指令:一上来就加载 getSum() 方法的返回值()
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0030.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0030.png">
@@ -841,7 +841,7 @@ SourceFile: "DynamicLinkingTest.java"
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0031.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0031.png">
@@ -1136,7 +1136,7 @@ interface MethodInterface {
Son 类中 show() 方法的字节码指令如下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0032.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0032.png">
@@ -1176,7 +1176,7 @@ public class Lambda {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0033.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0033.png">
@@ -1233,7 +1233,7 @@ JavaString info = "mogu blog"; (Java是静态类型语言的会先
如图所示:如果类中重写了方法,那么调用的时候,就会直接在该类的虚方法表中查找
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0034.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0034.png">
1、比如说son在调用toString的时候Son没有重写过Son的父类Father也没有重写过那就直接调用Object类的toString。那么就直接在虚方法表里指明toString直接指向Object类。
@@ -1243,24 +1243,24 @@ JavaString info = "mogu blog"; (Java是静态类型语言的会先
**例子2**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0035.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0035.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0036.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0036.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0037.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0037.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0038.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0038.jpg">
方法返回地址
--------
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0039.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0039.png">
> 在一些帖子里,方法返回地址、动态链接、一些附加信息 也叫做帧数据区
@@ -1309,7 +1309,7 @@ JavaString info = "mogu blog"; (Java是静态类型语言的会先
2. 方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0040.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0040.png">
@@ -1321,7 +1321,7 @@ JavaString info = "mogu blog"; (Java是静态类型语言的会先
* target :出现异常跳转至地址为 11 的指令执行
* type :捕获异常的类型
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0041.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_004/0041.png">

View File

@@ -27,7 +27,7 @@ date: 2020-11-11 20:38:42
1. 堆针对一个JVM进程来说是唯一的。也就是**一个进程只有一个JVM实例**一个JVM实例中就有一个运行时数据区一个运行时数据区只有一个堆和一个方法区。
2. 但是**进程包含多个线程,他们是共享同一堆空间的**。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0001.png">
@@ -68,7 +68,7 @@ public class SimpleHeap {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0002.png">
@@ -88,13 +88,13 @@ public class SimpleHeap {
约定:新生区 <> 新生代 <> 年轻代 、 养老区 <> 老年区 <> 老年代、 永久区 <\> 永久代
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0003.png">
2. 堆空间内部结构JDK1.8之前从永久代 替换成 元空间
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0004.png">
@@ -122,17 +122,17 @@ public class HeapDemo {
1、双击jdk目录下的这个文件
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0005.png">
2、工具 -> 插件 -> 安装Visual GC插件
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0006.png">
3、运行上面的代码
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0007.png">
@@ -215,7 +215,7 @@ public class HeapSpaceInitial {
设置下参数再看
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0008.png">
```java
public class HeapSpaceInitial {
@@ -250,7 +250,7 @@ public class HeapSpaceInitial {
**方式一: jps / jstat -gc 进程id**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0009.png">
> jps查看java进程
>
@@ -285,7 +285,7 @@ OU: 老年代使用的量
**方式二:-XX:+PrintGCDetails**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0010.png">
@@ -333,11 +333,11 @@ Process finished with exit code 1
2、堆内存变化图
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0011.png">
3、原因大对象导致堆内存溢出
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0012.png">
<img src="https://npm.elemecdn.com/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区
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0013.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0014.png">
- 配置新生代与老年代在堆结构的占比
@@ -383,7 +383,7 @@ Process finished with exit code 1
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0015.png">
@@ -439,7 +439,7 @@ public class EdenSurvivorTest {
1、我们创建的对象一般都是存放在Eden区的**当我们Eden区满了后就会触发GC操作**,一般被称为 YGC / Minor GC操作
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0016.png">
2、当我们进行一次垃圾收集后红色的对象将会被回收而绿色的独享还被占用着存放在S0(Survivor From)区。同时我们给每个对象设置了一个年龄计数器,经过一次回收后还存在的对象,将其年龄加 1。
@@ -453,11 +453,11 @@ public class EdenSurvivorTest {
>
> 3、也就是说s0区和s1区在互相转换。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0017.png">
4、我们继续不断的进行对象生成和垃圾回收当Survivor中的对象的年龄达到15的时候将会触发一次 Promotion 晋升的操作,也就是将年轻代中的对象晋升到老年代中
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0018.png">
关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。
@@ -475,7 +475,7 @@ public class EdenSurvivorTest {
* 那万一老年代都放不下则先触发FullGC ,再看看能不能放下,放得下最好,但如果还是放不下,那只能报 OOM
3. 如果 Eden 区满了,将对象往幸存区拷贝时,发现幸存区放不下啦,那只能便宜了某些新对象,让他们直接晋升至老年区
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0019.png">
### 常用调优工具
@@ -528,7 +528,7 @@ GC分类
3. Minor GC会引发STWStop The World暂停其它用户的线程等垃圾回收结束用户线程才恢复运行
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0020.png">
@@ -649,7 +649,7 @@ Heap
* 新生代有Eden、两块大小相同的survivor又称为from/to或s0/s1构成to总为空。
* 老年代存放新生代中经历多次GC仍然存活的对象。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0021.png">
@@ -661,7 +661,7 @@ Heap
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0022.png">
@@ -711,7 +711,7 @@ TLABThread Local Allocation Buffer
2. 多线程同时分配内存时使用TLAB可以避免一系列的非线程安全问题同时还能够提升内存分配的吞吐量因此我们可以将这种内存分配方式称之为**快速分配策略**。
3. 据我所知所有OpenJDK衍生出来的JVM都提供了TLAB的设计。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0023.png">
1、每个线程都有一个TLAB空间
@@ -741,7 +741,7 @@ TLABThread Local Allocation Buffer
**TLAB 分配过程**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_005/0024.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_005/0024.png">

View File

@@ -24,7 +24,7 @@ date: 2020-11-13 19:38:42
ThreadLocal如何保证多个线程在并发环境下的安全性典型场景就是数据库连接管理以及会话管理。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0001.png">
**栈、堆、方法区的交互关系**
@@ -35,7 +35,7 @@ ThreadLocal如何保证多个线程在并发环境下的安全性典型场
3. 真正的 person 对象存放在 Java 堆中
4. 在 person 对象中,有个指针指向方法区中的 person 类型数据,表明这个 person 对象是用方法区中的 Person 类 new 出来的
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0002.png">
方法区的理解
--------
@@ -47,7 +47,7 @@ ThreadLocal如何保证多个线程在并发环境下的安全性典型场
1. 《Java虚拟机规范》中明确说明尽管所有的方法区在逻辑上是属于堆的一部分但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpotJVM而言方法区还有一个别名叫做Non-Heap非堆目的就是要和堆分开。
3. 所以,**方法区可以看作是一块独立于Java堆的内存空间**。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0003.png">
@@ -87,7 +87,7 @@ public class MethodAreaDemo {
简单的程序加载了1600多个类
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0004.png">
@@ -103,7 +103,7 @@ public class MethodAreaDemo {
5. 永久代、元空间二者并不只是名字变了,内部结构也调整了
6. 根据《Java虚拟机规范》的规定如果方法区无法满足新的内存分配需求时将抛出OOM异常
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0005.png">
@@ -120,7 +120,7 @@ public class MethodAreaDemo {
2. -XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M64位机器模式是82M
3. 当JVM加载的类信息容量超过了这个值会报异常OutofMemoryError:PermGen space。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0006.png">
### JDK8及以后(元空间)
@@ -228,11 +228,11 @@ Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
#### 概念
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0007.png">
《深入理解Java虚拟机》书中对方法区Method Area存储内容描述如下它用于存储已被虚拟机加载的**类型信息、常量、静态变量、即时编译器编译后的代码缓存**等。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0008.png">
@@ -714,7 +714,7 @@ public static int count;
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0009.png">
1. 方法区,内部包含了运行时常量池
2. 字节码文件内部包含了常量池。之前的字节码文件中已经看到了很多Constant pool的东西这个就是常量池
@@ -728,7 +728,7 @@ public static int count;
1. 一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外。还包含一项信息就是**常量池表****Constant Pool Table**),包括各种字面量和对类型、域和方法的符号引用。
2. 字面量: 10 “我是某某”这种数字和字符串都是字面量
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0010.png">
**为什么需要常量池?**
@@ -749,7 +749,7 @@ public static int count;
2. 比如说我们这个文件中有6个地方用到了"hello"这个字符串如果不用常量池就需要在6个地方全写一遍造成臃肿。我们可以将"hello"等所需用到的结构信息记录在常量池中,并通过**引用的方式**,来加载、调用所需的结构
4. 这里的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0011.png">
**常量池中有啥?**
@@ -924,69 +924,69 @@ SourceFile: "MethodAreaDemo.java"
1、初始状态
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0012.png">
2、首先将操作数500压入操作数栈中
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0013.png">
3、然后操作数 500 从操作数栈中取出,存储到局部变量表中索引为 1 的位置
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0014.png">
4、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0015.png">
5、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0016.png">
6、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0017.png">
7、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0018.png">
8、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0019.png">
9、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0020.png">
10、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0021.png">
11、图片写错了是#25和#26获得System类
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0022.png">
12、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0023.png">
13、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0024.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0024.png">
15、执行加法运算后将计算结果放在操作数栈顶
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0025.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0025.png">
16、就是真正的打印
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0026.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0026.png">
17、
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0027.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0027.png">
@@ -1021,7 +1021,7 @@ SourceFile: "MethodAreaDemo.java"
方法区由永久代实现,使用 JVM 虚拟机内存(虚拟的内存)
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0028.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0028.png">
@@ -1029,7 +1029,7 @@ SourceFile: "MethodAreaDemo.java"
方法区由永久代实现,使用 JVM 虚拟机内存
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0029.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0029.png">
@@ -1037,7 +1037,7 @@ SourceFile: "MethodAreaDemo.java"
方法区由元空间实现,使用物理机本地内存
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0030.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0030.png">
@@ -1095,15 +1095,15 @@ public class StaticFieldTest {
JDK6环境下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0031.png" alt="image-20201113224231761" />
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0031.png" alt="image-20201113224231761" />
JDK7环境下
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0032.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0032.png">
JDK8环境
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0033.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0033.png">
@@ -1150,7 +1150,7 @@ public class StaticObjTest {
4、测试发现三个对象的数据在内存中的地址都落在Eden区范围内所以结论**只要是对象实例必然会在Java堆中分配**。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0034.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0034.png">
> 1、0x00007f32c7800000(Eden区的起始地址) ---- 0x00007f32c7b50000(Eden区的终止地址)
>
@@ -1162,7 +1162,7 @@ public class StaticObjTest {
5、接着找到了一个引用该staticObj对象的地方是在一个java.lang.Class的实例里并且给出了这个实例的地址通过Inspector查看该对象实例可以清楚看到这确实是一个java.lang.Class类型的对象实例里面有一个名为staticobj的实例字段
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0035.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0035.png">
从《Java虚拟机规范》所定义的概念模型来看所有Class相关的信息都应该存放在方法区之中但方法区该如何实现《Java虚拟机规范》并未做出规定这就成了一件允许不同虚拟机自己灵活把握的事情。JDK7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对象存放在一起**存储于Java堆之中**,从我们的实验中也明确验证了这一点
@@ -1217,7 +1217,7 @@ public class StaticObjTest {
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0036.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0036.png">
@@ -1271,7 +1271,7 @@ public class BufferTest {
直接占用了 1G 的本地内存
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0037.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0037.jpg">
@@ -1281,13 +1281,13 @@ public class BufferTest {
原来采用BIO的架构在读写本地文件时我们需要从用户态切换成内核态
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0038.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0038.png">
**直接缓冲区NIO**
NIO 直接操作物理磁盘,省去了中间过程
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0039.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0039.png">
### 直接内存与 OOM
@@ -1351,7 +1351,7 @@ Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_006/0040.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_006/0040.jpg">

View File

@@ -36,7 +36,7 @@ date: 2020-11-14 19:38:42
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0001.png">
### 对象创建的方式
@@ -211,7 +211,7 @@ class Account{
对象的内存布局
---------
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0002.png">
@@ -242,7 +242,7 @@ class Account{
图解内存布局
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0003.png">
@@ -251,7 +251,7 @@ class Account{
**JVM是如何通过栈帧中的对象引用访问到其内部的对象实例呢**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0004.png">
定位通过栈上reference访问
@@ -262,7 +262,7 @@ class Account{
1. 缺点:在堆空间中开辟了一块空间作为句柄池,句柄池本身也会占用空间;通过两次指针访问才能访问到堆中的对象,效率低
2. 优点reference中存储稳定句柄地址对象被移动垃圾收集时移动对象很普遍时只会改变句柄中实例数据指针即可reference本身不需要被修改
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0005.png">
@@ -271,4 +271,4 @@ class Account{
1. 优点:直接指针是局部变量表中的引用,直接指向堆中的实例,在对象实例中有类型指针,指向的是方法区中的对象类型数据
2. 缺点:对象被移动(垃圾收集时移动对象很普遍)时需要修改 reference 的值
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_007/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_007/0006.png">

View File

@@ -23,7 +23,7 @@ date: 2020-11-15 19:48:42
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0001.png">
### 执行引擎概述
@@ -34,7 +34,7 @@ date: 2020-11-15 19:48:42
3. JVM的主要任务是负责**装载字节码到其内部**但字节码并不能够直接运行在操作系统之上因为字节码指令并非等价于本地机器指令它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表以及其他辅助信息。
4. 那么如果想要让一个Java程序运行起来执行引擎Execution Engine的任务就是**将字节码指令解释/编译为对应平台上的本地机器指令才可以**。简单来说JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0002.png">
1、前端编译从Java程序员-字节码文件的这个过程叫前端编译
@@ -51,7 +51,7 @@ date: 2020-11-15 19:48:42
3. 当然方法在执行的过程中执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息以及通过对象头中的元数据指针定位到目标对象的类型信息。
4. 从外观上来看所有的Java虚拟机的执行引擎输入、处理、输出都是一致的输入的是字节码二进制流处理过程是字节码解析执行、即时编译的等效过程输出的是执行过程。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0003.png">
@@ -71,18 +71,18 @@ Java代码编译和执行过程
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0004.png">
3. javac编译器前端编译器流程图如下所示
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0005.png">
4. Java字节码的执行是由JVM执行引擎来完成流程图如下所示
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0006.png">
@@ -105,7 +105,7 @@ Java代码编译和执行过程
**用图总结一下**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0007.png">
机器码 指令 汇编语言
-------------
@@ -164,7 +164,7 @@ Java代码编译和执行过程
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0008.png">
@@ -193,7 +193,7 @@ Java代码编译和执行过程
2. 汇编过程:实际上指把汇编语言代码翻译成目标机器指令的过程。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0009.png">
@@ -211,7 +211,7 @@ Java代码编译和执行过程
3. 当一条字节码指令被解释执行完成后接着再根据PC寄存器中记录的下一条需要被执行的字节码指令执行解释操作。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0010.png">
@@ -288,7 +288,7 @@ Java代码编译和执行过程
2. 在生产环境发布过程中以分批的方式进行发布根据机器数量划分成多个批次每个批次的机器数至多占到整个集群的1/8。曾经有这样的故障案例某程序员在发布平台进行分批发布在输入发布总批数时误填写成分为两批发布。如果是热机状态在正常情况下一半的机器可以勉强承载流量但由于刚启动的JVM均是解释执行还没有进行热点代码统计和JIT动态编译导致机器启动之后当前1/2发布成功的服务器马上全部宕机此故障说明了JIT的存在。—**阿里团队**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0011.png">
@@ -317,7 +317,7 @@ public class JITTest {
通过 JVisualVM 查看 JIT 编译器执行的编译次数
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0012.png">
@@ -369,7 +369,7 @@ public class JITTest {
* 如果已超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。
* 如果未超过阈值,则使用解释器对字节码文件解释执行
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0013.png">
@@ -387,7 +387,7 @@ public class JITTest {
它的作用是统计一个方法中循环体代码执行的次数在字节码中遇到控制流向后跳转的指令称为“回边”Back Edge。显然建立回边计数器统计的目的就是为了触发OSR编译。
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0014.png">
@@ -401,7 +401,7 @@ public class JITTest {
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_008/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_008/0015.png">

View File

@@ -166,11 +166,11 @@ str 的内容并没有变“test ok” 位于字符串常量池中的另一
4. 在JDK7中StringTable的长度默认值是60013StringTablesize设置没有要求
5. 在JDK8中StringTable的长度默认值是60013StringTable可以设置的最小值为1009
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0001.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0002.png">
@@ -276,11 +276,11 @@ String 的内存分配
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0003.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0004.png">
@@ -382,23 +382,23 @@ public class StringTest4 {
1、程序启动时已经加载了 2293 个字符串常量
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0005.png">
2、加载了一个换行符println所以多了一个
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0006.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0006.jpg">
3、加载了字符串常量 “1”~“9”
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0007.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0007.jpg">
4、加载字符串常量 “10”
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0008.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0008.jpg">
5、之后的字符串"1" 到 "10"不会再次加载
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0009.png">
@@ -425,7 +425,7 @@ class Memory {
分析运行时内存foo() 方法是实例方法,其实图中少了一个 this 局部变量)
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0010.png">
@@ -493,7 +493,7 @@ class Memory {
IDEA 反编译 class 文件后,来看这个问题
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0011.png">
@@ -929,7 +929,7 @@ public class StringNewTest {
5. `23 ldc #8 <b>` :在字符串常量池中放入 “b”如果之前字符串常量池中没有 “b” 的话)
6. `31 invokevirtual #9 <java/lang/StringBuilder.toString>` :调用 StringBuilder 的 toString() 方法,会生成一个 String 对象
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0012.png">
@@ -988,13 +988,13 @@ JDK6 :正常眼光判断即可
* new String() 即在堆中
* str.intern() 则把字符串放入常量池中
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0013.png">
JDK7及后续版本**注意大坑**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0014.png">
@@ -1053,11 +1053,11 @@ public class StringExer1 {
**JDK6**
![image-20201116113423492](https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0015.png)
![image-20201116113423492](https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0015.png)
**JDK7/8**
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0016.png">
@@ -1080,7 +1080,7 @@ public class StringExer1 {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0017.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0017.png">
**练习3**
@@ -1169,11 +1169,11 @@ public class StringIntern2 {
arr[i] = new String(String.valueOf(data[i % data.length]));
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0018.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0019.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0019.png">
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();
```
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0020.png" >
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0020.png" >
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0021.png">
@@ -1218,11 +1218,11 @@ public class StringGCTest {
* Number of entries 和 Number of literals 明显没有 100000
* 以上两点均说明 StringTable 区发生了垃圾回收
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>

View File

@@ -20,7 +20,7 @@ date: 2020-10-19 22:15:58
> 本篇文章只讲解比较重要的
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0001.png">
@@ -308,13 +308,13 @@ public class LambdaTest1 {
**核心函数式接口**
<img src="https://img.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/0002.png">
**其它函数式接口**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0003.png">
@@ -902,7 +902,7 @@ Stream到底是什么呢
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0004.png">
@@ -1219,7 +1219,7 @@ public class StreamAPITest2 {
## 常用API
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Java8_New_Features/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Java8_New_Features/0005.png">

View File

@@ -1038,7 +1038,7 @@ class Dog extends Animal {
`test1()`在编译时就会飘红
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Generic/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Generic/0001.png">
@@ -1290,7 +1290,7 @@ public class Test_difference {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_Basis/Generic/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_Basis/Generic/0002.png">
### 区别3通配符可以使用超类限定而T不行

View File

@@ -202,7 +202,7 @@ hadoop2
大致是这样的一个结构
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
- 每个链表就算哈希表的桶bucket
- 链表的节点值就算一个键值对
@@ -398,7 +398,7 @@ static class Entry<K,V> implements Map.Entry<K,V> {
`HashMap`中的数组元素 & 链表节点 采用 `Entry`类实现
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0001.png">
1、一个正方形代表一个Entry对象同时也代表一个键值对。
@@ -769,15 +769,15 @@ void transfer(Entry[] newTable, boolean rehash) {
大概画了一下图:
<img src="https://img.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/0002.png"/>
<img src="https://img.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/0003.png">
<img src="https://img.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/0004.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0005.png">
@@ -871,7 +871,7 @@ void transfer(Entry[] newTable, boolean rehash) {
**hashmap初始状态**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0006.png">
@@ -897,7 +897,7 @@ void transfer(Entry[] newTable, boolean rehash) {
**两个线程调用完毕之后hashmap目前是这样的。**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0007.png">
@@ -918,9 +918,9 @@ void transfer(Entry[] newTable, boolean rehash) {
3、来看下此时内存里的状态
<img src="https://img.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/0008.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0009.png">
## 步骤4
@@ -957,7 +957,7 @@ void transfer(Entry[] newTable, boolean rehash) {
2、线程2直接**扩容完毕**那么完成后的状态是这样【假设e2和e3还是hash到同一个位置】
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0010.png">
3、线程1还是原来的状态
@@ -967,11 +967,11 @@ void transfer(Entry[] newTable, boolean rehash) {
目前两个线程里的新数组是这样的
<img src="https://img.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/0011.png">
为了方便后面观看,我画成这样。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0012.png">
@@ -1015,7 +1015,7 @@ void transfer(Entry[] newTable, boolean rehash) {
也就变成了下面这个样子。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0013.png">
@@ -1061,7 +1061,7 @@ void transfer(Entry[] newTable, boolean rehash) {
执行完,变成这样。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0014.png">
<img src="https://npm.elemecdn.com/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
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK7/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK7/0015.png">
这样就形成了循环链表再get()数据就会陷入死循环。

View File

@@ -27,7 +27,7 @@ date: 2020-11-01 10:22:05
在JDK8中优化了HashMap的数据结构引入了红黑树。即HashMap的数据结构数组+链表+红黑树。HashMap变成了这样。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0001.png">
### 为什么要引入红黑树
@@ -456,7 +456,7 @@ Process finished with exit code 0
JDK8 hash的运算原理高位参与低位运算使得hash更加均匀。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0002.png">
@@ -569,7 +569,7 @@ JDK8 hash的运算原理高位参与低位运算使得hash更加均匀。
JDK8扩容时数据在数组下标的计算方式
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_collection/HashMap/JDK8/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_collection/HashMap/JDK8/0003.png">
* `JDK8`根据此结论作出的新元素存储位置计算规则非常简单,提高了扩容效率。

View File

@@ -198,7 +198,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
1. 这种设计思路虽然可以解决问题,但不够优美。因为除了鸵鸟之外,不会飞的鸟还有很多,比如企鹅。对于这些不会飞的鸟来说,我们都需要重写 fly() 方法抛出异常。这样的设计一方面徒增了编码的工作量另一方面也违背了我们之后要讲的最小知识原则Least Knowledge Principle也叫最少知识原则或者迪米特法则暴露不该暴露的接口给外部增加了类使用过程中被误用的概率。
2. 你可能又会说,那我们再通过 AbstractBird 类派生出两个更加细分的抽象类:会飞的鸟类 AbstractFlyableBird 和不会飞的鸟类 AbstractUnFlyableBird让麻雀、乌鸦这些会飞的鸟都继承 AbstractFlyableBird让鸵鸟、企鹅这些不会飞的鸟都继承 AbstractUnFlyableBird 类,不就可以了吗?具体的继承关系如下图所示:
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0001.png"/>
@@ -206,7 +206,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
2. 是否会飞是否会叫两个行为搭配起来会产生四种情况会飞会叫、不会飞会叫、会飞不会叫、不会飞不会叫。如果我们继续沿用刚才的设计思路那就需要再定义四个抽象类AbstractFlyableTweetableBird、AbstractFlyableUnTweetableBird、AbstractUnFlyableTweetableBird、AbstractUnFlyableUnTweetableBird
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0002.png"/>
@@ -376,7 +376,7 @@ demofunction(client);
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/design_ideas/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_ideas/0003.png"/>

View File

@@ -724,7 +724,7 @@ public class UserRepo {
前面也提到,“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。关于这一点,我画了一张对比图来解释。图中左边部分的代码结构是“高内聚、松耦合”;右边部分正好相反,是“低内聚、紧耦合”。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/design_principles/0001.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/design_principles/0001.png" />

View File

@@ -727,7 +727,7 @@ public class BeansFactory {
1. 在平时的开发中,创建一个对象最常用的方式是,使用 new 关键字调用类的构造函数来完成。我的问题是,什么情况下这种方式就不适用了,就需要采用建造者模式来创建对象呢?你可以先思考一下,下面我通过一个例子来带你看一下。
2. 假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中有以下几个成员变量也就是可配置项。现在请你编写代码实现这个 ResourcePoolConfig 类。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0001.png" />
<img src="https://npm.elemecdn.com/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 就可以了。
3. 不过,我们还有另外一个系统 B专门用来分析搜索日志定期比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0002.png"/>
@@ -1150,15 +1150,15 @@ public class Demo {
我们来看,在内存中,用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,大致结构如下所示。从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外的内存空间中。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0003.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0003.png" />
浅拷贝和深拷贝的区别在于浅拷贝只会复制图中的索引散列表不会复制数据SearchWord 对象本身。相反深拷贝不仅仅会复制索引还会复制数据本身。浅拷贝得到的对象newKeywords跟原始对象currentKeywords共享数据SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。具体的对比如下图所示:
<img src="https://img.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/0004.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/creational/03.02/0005.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/creational/03.02/0005.png" />

View File

@@ -608,7 +608,7 @@ IUserController userController = (IUserController) proxy.createProxy(new UserCon
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0001.png"/>
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0007.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0007.png"/>
1. 扩展性问题(**类爆炸**),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
2. 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
@@ -933,7 +933,7 @@ public class DriverManager {
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0002.png">
@@ -1112,7 +1112,7 @@ public class TrivialNotification extends Notification {
### 方案一
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0003.png"/>
@@ -1127,7 +1127,7 @@ public class TrivialNotification extends Notification {
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0004.png">
@@ -1142,7 +1142,7 @@ public class TrivialNotification extends Notification {
### 装饰器模式代码
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0005.png"/>
#### Drink【抽象类-主体Component】
@@ -1390,7 +1390,7 @@ Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读
针对不同的读取和写入场景Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.01/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0006.png"/>

View File

@@ -75,7 +75,7 @@ date: 2021-07-05 16:51:58
### 传统方案
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.02/0001.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0001.png" />
1. 在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程 混乱,没有清晰的过程 不利于在 ClientTest 中,去维护对子系统的操作
@@ -623,7 +623,7 @@ public class Demo {
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/structural_type/04.02/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0002.png"/>

View File

@@ -34,7 +34,7 @@ date: 2021-07-14 16:51:58
### 方案一
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.01/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0001.png"/>
@@ -721,11 +721,11 @@ public DObserver{
1. Guava EventBus 的功能我们已经讲清楚了,总体上来说,还是比较简单的。接下来,我们就重复造轮子,“山寨”一个 EventBus 出来。
2. 我们重点来看EventBus 中两个核心函数 register() 和 post() 的实现原理。弄懂了它们,基本上就弄懂了整个 EventBus 框架。下面两张图是这两个函数的实现原理图。
<img src="https://img.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/0002.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.01/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0003.png"/>

View File

@@ -1171,7 +1171,7 @@ public class SensitiveWordFilter {
Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器比如Tomcat、Jetty 等),都支持过滤器功能。为了帮助你理解,我画了一张示意图阐述它的工作原理,如下所示。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.02/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0001.png"/>
在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的示例代码,如下所示。添加一个过滤器,我们只需要定义一个实现 javax.servlet.Filter 接口的过滤器类,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置,创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。
@@ -1279,7 +1279,7 @@ ApplicationFilterChain 中的 doFilter() 函数的代码实现比较有技巧,
1. 刚刚讲了 Servlet Filter现在我们来讲一个功能上跟它非常类似的东西Spring Interceptor翻译成中文就是拦截器。尽管英文单词和中文翻译都不同但这两者基本上可以看作一个概念都用来实现对 HTTP 请求进行拦截处理。
2. 它们不同之处在于Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter然后再经过 Spring Interceptor最后到达具体的业务代码中。我画了一张图来阐述一个请求的处理流程具体如下所示。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.02/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0002.png"/>

View File

@@ -30,7 +30,7 @@ date: 2021-08-02 15:51:58
4. 实际上,马里奥形态的转变就是一个状态机。其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动作”。比如,吃蘑菇这个事件,会触发状态的转移:从小马里奥转移到超级马里奥,以及触发动作的执行(增加 100 积分)。
5. 为了方便接下来的讲解,我对游戏背景做了简化,只保留了部分状态和事件。简化之后的状态转移如下图所示:
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0001.png"/>
@@ -180,7 +180,7 @@ public class MarioStateMachine {
1. 实际上,上面这种实现方法有点类似 hard code对于复杂的状态机来说不适用而状态机的第二种实现方式查表法就更加合适了。接下来我们就一块儿来看下如何利用查表法来补全骨架代码。
2. 实际上,除了用状态转移图来表示之外,状态机还可以用二维表来表示,如下所示。在这个二维表中,第一维表示当前状态,第二维表示事件,值表示当前状态经过事件之后,转移到的新状态及其执行的动作。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0002.png"/>
3. 相对于分支逻辑的实现方式,查表法的代码实现更加清晰,可读性和可维护性更好。当修改状态机时,我们只需要修改 transitionTable 和 actionTable 两个二维数组即可。实际上,如果我们把这两个二维数组存储在配置文件中,当需要修改状态机时,我们甚至可以不修改任何代码,只需要修改配置文件就可以了。具体的代码如下所示:
@@ -511,7 +511,7 @@ public class MarioStateMachine {
2. 在开篇中我们讲到,它用来遍历集合对象。这里说的“集合对象”也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如数组、链表、树、图、跳表。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
3. 迭代器是用来遍历容器的,所以,一个完整的迭代器模式一般会涉及**容器和容器迭代器**部分两部分内容。为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类。对于迭代器模式,我画了一张简单的类图,你可以看一看,先有个大致的印象。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0003.png">
@@ -629,7 +629,7 @@ public class Demo {
2. 结合刚刚的例子,我们来总结一下迭代器的设计思路。总结下来就三句话:迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。
3. 这里我画了一张类图,如下所示。实际上就是对上面那张类图的细化,你可以结合着一块看。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0004.png"/>
<img src="https://npm.elemecdn.com/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 遍历不到了。
5. 对于上面的描述,我画了一张图,你可以对照着理解。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0005.png"/>
6. 不过,如果第 57 行代码删除的不是游标前面的元素(元素 a以及游标所在位置的元素元素 b而是游标后面的元素元素 c 和 d这样就不会存在任何问题了不会存在某个元素遍历不到的情况了。
7. 所以,我们前面说,在遍历的过程中删除集合元素,结果是不可预期的,有时候没问题(删除元素 c 或 d有时候就有问题删除元素 a 或 b这个要视情况而定到底删除的是哪个位置的元素就是这个意思。
@@ -778,7 +778,7 @@ public class Demo {
10. 跟删除情况类似,如果我们在游标的后面添加元素,就不会存在任何问题。所以,在遍历的同时添加集合元素也是一种不可预期行为。
11. 同样,对于上面的添加元素的情况,我们也画了一张图,如下所示,你可以对照着理解。
<img src="https://img.imlql.cn/youthlql@1.0.0/design_patterns/behavior_type/05.03/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0006.png"/>

View File

@@ -97,7 +97,7 @@ Dubbo网关参考[https://github.com/apache/dubbo-proxy](https://github.com/a
### 基本原理
<img src="https://img.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_yi_jie/0001.png"/>
<img src="https://npm.elemecdn.com/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里和管理台写了相同的配置以管理台的为主。
<img src="https://img.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0001.png"/>
3. **动态配置**这里,也可以很方便的替代服务提供者@service注解上标注的那些配置。管理台是实时生效的如果改代码里的@service还需要重启服务。
<img src="https://img.imlql.cn/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.1/rpc/dubbo/v1/01_di_er_jie/0002.png"/>
很多配置都可以在管理台上配。管理台上写的配置会持久化在**你配置的配置中心**里。只有注册中心里的服务提供者信息不持久化如果注册中心是zookeeper那么服务提供者在zk上就是临时节点。

View File

@@ -243,7 +243,7 @@ public class Application {
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/Dubbo监听架构图.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/Dubbo监听架构图.png"/>
### 源码分析-解析@Reference注解上的配置
@@ -438,19 +438,19 @@ public class Application {
```
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210915224055718.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210915224055718.png"/>
Dubbo官方给的Demo没有配置URL所以这里就是NULL
<img src="https://img.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-20210920151657492.png"/>
<img src="https://img.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-20210920151857950.png" />
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152003415.png"/>
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152813300.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920152813300.png"/>
#### RegistryDirectory
@@ -641,7 +641,7 @@ Dubbo官方给的Demo没有配置URL所以这里就是NULL
3. 看下面的截图registry属性是zookeeper的URL所以应该是要调用ZookeeperRegistry的subscribe()方法但是ZookeeperRegistry没有这个方法所以我们就要找它的父类了也就是FailbackRegistry
4. 然后再调用doSubscribe()ZookeeperRegistry重写了此方法很明显这是个模板模式。
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154023711.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154023711.png" />
#### FailbackRegistry
@@ -843,7 +843,7 @@ Dubbo官方给的Demo没有配置URL所以这里就是NULL
最终走到了这一步
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154648609.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210920154648609.png" />
#### RegistryDirectory
@@ -1097,7 +1097,7 @@ consumer://192.168.0.100/org.apache.dubbo.demo.DemoService?application=dubbo-dem
到此,路由链构造完毕。
<img src="https://img.imlql.cn/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210919190129223.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/06_di_liu_jie/image-20210919190129223.png"/>

View File

@@ -31,13 +31,13 @@ date: 2020-10-05 22:40:58
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
说明线程作为CPU调度和执行的单位每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。
<img src="https://img.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/0001.png">
补充:
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0002.png">
<img src="https://npm.elemecdn.com/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类来处理。
2、在进行Thread启动多线程的时候调用的是start()方法而后找到的是run()方法但通过Thread类的构造方法传递了一个Runnable接口对象的时候那么该接口对象将被Thread类中的target属性所保存在start()方法执行的时候会调用Thread类中的run()方法。而这个run()方法去调用实现了Runnable接口的那个类所重写过run()方法进而执行相应的逻辑。多线程开发的本质实质上是在于多个线程可以进行同一资源的抢占那么Thread主要描述的是线程而资源的描述是通过Runnable完成的。如下图所示
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0003.png">
@@ -644,7 +644,7 @@ public void run() {
1、如果直接调用run()方法,相当于就是简单的调用一个普通方法。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0004.png">
2、run()的调用是在start0()这个Native C++方法里调用的
@@ -654,11 +654,11 @@ public void run() {
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态这几个状态在Java源码中用枚举来表示。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0005.png">
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0006.png">
> 图中 wait到 runnable状态的转换中`join`实际上是`Thread`类的方法,但这里写成了`Object`。
@@ -699,7 +699,7 @@ public static void main(String[] args) {
2、当JVM启动后实际有多个线程但是至少有一个非守护线程比如main线程
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0007.png">
- FinalizerGC守护线程
@@ -1574,7 +1574,7 @@ volatile自己虽然不能保证原子性但是和CAS结合起来就可以保
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0009.png">
@@ -1782,7 +1782,7 @@ public Unsafe getUnsafe() throws IllegalAccessException {
Unsafe的功能如下图
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0008.png">
## CAS相关

View File

@@ -1869,7 +1869,7 @@ public class ForkJoinRecursiveAction {
ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类`RecursiveAction``RecursiveTask`。然后通过fork()方法去分配任务执行任务通过join()方法汇总任务结果。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0001.png">
## 小总结

View File

@@ -563,7 +563,7 @@ public int getUnarrivedParties()
根据上面的代码,我们可以画出下面这个很简单的图:
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0002.png">
这棵树上有 7 个 phaser 实例,每个 phaser 实例在构造的时候,都指定了 parties 为 5但是对于每个拥有子节点的节点来说每个子节点都是它的一个 party我们可以通过 phaser.getRegisteredParties() 得到每个节点的 parties 数量:
@@ -952,7 +952,7 @@ public class ThreadPoolDemo {
## 线程池的底层工作流程
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0003.png">
1、创建线程池后等待请求任务
@@ -1646,7 +1646,7 @@ public class ExecutorCompletionService<V> implements CompletionService<V> {
**执行流程:**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Third_stage/0004.png">

View File

@@ -291,7 +291,7 @@ d = e - f ;
由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
@@ -343,7 +343,7 @@ int c = a + b;
冯诺依曼,提出计算机由五大组成部分,输入设备,输出设备存储器,控制器,运算器。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0002.png">
<img src="https://npm.elemecdn.com/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然后依次是 L2L3和主内存CPU缓存模型如图下图所示。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
CPU Cache分成了三个级别: L1 L2 L3。级别越小越接近CPU速度也更快同时也代表着容量越小。速度越快的价格越贵。
@@ -377,7 +377,7 @@ CPU Cache分成了三个级别: L1 L2 L3。级别越小越接近CPU
3、L3 Cache是三级缓存中大的一级例如12MB同时也是缓存中慢的一级在同一个CPU插槽 之间的核共享一个L3 Cache。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
上面的图中有一个Latency指标。比如Memory这个指标为59.4ns表示CPU在操作内存的时候有59.4ns的延迟一级缓存最快只有1.2ns。
@@ -409,7 +409,7 @@ Cache的出现是为了解决CPU直接访问内存效率低下问题的。
每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有的操 作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接 访问对方工作内存中的变量。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
Java的线程不能直接在主内存中操作共享变量。而是首先将主内存中的共享变量赋值到自己的工作内存中再进行操作操作完成之后刷回主内存。
@@ -424,7 +424,7 @@ Java内存模型是一套在多线程读写共享数据时对共享数据的
JMM内存模型与CPU硬件内存架构的关系
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
工作内存可能对应CPU寄存器也可能对应CPU缓存也可能对应内存。
@@ -434,9 +434,9 @@ JMM内存模型与CPU硬件内存架构的关系
## 再谈可见性
<img src="https://img.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/0007.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
1、图中所示是 个双核 CPU 系统架构 每个核有自己的控制器和运算器其中控制器包含一组寄存器和操作控制器运算器执行算术逻辅运算。每个核都有自己的1级缓存在有些架构里面还有1个所有 CPU 共享的2级缓存。 那么 Java 内存模型里面的工作内存,就对应这里的 Ll 或者 L2 存或者 CPU 寄存器。
@@ -452,7 +452,7 @@ JMM内存模型与CPU硬件内存架构的关系
为了保证数据交互时数据的正确性Java内存模型中定义了8种操作来完成这个交互过程这8种操作本身都是原子性的。虚拟机实现时必须保证下面 提及的每一种操作都是原子的、不可再分的。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
> (1)lock:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
>
@@ -478,7 +478,7 @@ JMM内存模型与CPU硬件内存架构的关系
如果没有synchronized那就是下面这样的
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
@@ -589,7 +589,7 @@ volatile不保证原子性只保证可见性和禁止指令重排
## CPU术语介绍
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
@@ -717,7 +717,7 @@ public class VolatileExample {
**1、下面是保守策略下volatile写插入内存屏障后生成的指令序列示意图**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0012.png">
<img src="https://npm.elemecdn.com/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在实现上的一个特点首先确保正确性然后再去追求执行效率
@@ -725,7 +725,7 @@ public class VolatileExample {
**2、下面是在保守策略下volatile读插入内存屏障后生成的指令序列示意图**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
> 图中的LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。 LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。 上述volatile写和volatile读的内存屏障插入策略非常保守。在实际执行时只要不改变volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。
@@ -752,7 +752,7 @@ class VolatileBarrierExample {
针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
注意最后的StoreLoad屏障不能省略。因为第二个volatile写之后方法立即return。此时编译器可能无法准确断定后面是否会有volatile读或写为了安全起见编译器通常会在这里插入一个StoreLoad屏障。
@@ -766,7 +766,7 @@ class VolatileBarrierExample {
X86处理器仅会对写-读操作做重排序。X86不会对读-读、读-写和写-写操作 做重排序因此在X86处理器中会省略掉这3种操作类型对应的内存屏障。在X86中JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。这意味着在X86处理器中volatile写的开销比volatile读的开销会大很多因为执行StoreLoad屏障开销会比较大
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">

View File

@@ -18,7 +18,7 @@ date: 2020-10-07 22:10:58
# 可见性设计的硬件
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0016.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0016.png">
从硬件的级别来考虑一下可见性的问题
@@ -305,13 +305,13 @@ MESI协议规定了一组消息就说各个处理器在操作内存数据的
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0017.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0017.jpg">
## MESI-优化
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0018.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0018.png">
MESI协议如果每次写数据的时候都要发送invalidate消息等待所有处理器返回ack然后获取独占锁后才能写数据那可能就会导致性能很差了因为这个对共享变量的写操作实际上在硬件级别变成串行的了。所以为了解决这个问题硬件层面引入了写缓冲器和无效队列
@@ -449,7 +449,7 @@ int b = c; //load
## 相关术语
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0019.png">
<img src="https://npm.elemecdn.com/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如图所示。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0020.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0020.png">
原因可能是多个处理器同时从各自的缓存中读取变量i分别进行加1操作然后分别写入 系统内存中。那么想要保证读改写共享变量的操作是原子的就必须保证CPU1读改写共享 变量的时候CPU2不能操作缓存了该共享变量内存地址的缓存。

View File

@@ -357,17 +357,17 @@ monitorexit释放锁。 monitorexit插入在方法结束处和异常处JVM保
术语参考: http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html 在JVM中对象在内存中的布局分为三块区域对象头、实例数据和对齐填充。如下图所示
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0021.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0021.png">
## 对象头
当一个线程尝试访问synchronized修饰的代码块时它首先要获得锁那么这个锁到底存在哪里呢是 存在锁对象的对象头中的。 HotSpot采用instanceOopDesc和arrayOopDesc来描述对象头arrayOopDesc对象用来描述数组类型。instanceOopDesc的定义的在Hotspot源码的 instanceOop.hpp 文件中另外arrayOopDesc 的定义对应 arrayOop.hpp
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0022.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0022.png">
从instanceOopDesc代码中可以看到 instanceOopDesc继承自oopDescoopDesc的定义载Hotspot 源码中的 oop.hpp 文件中。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0023.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0023.png">
- 在普通实例对象中oopDesc的定义包含两个成员分别是 _mark 和 _metadata
@@ -383,21 +383,21 @@ monitorexit释放锁。 monitorexit插入在方法结束处和异常处JVM保
Mark Word用于存储对象自身的运行时数据如哈希码HashCode、GC分代年龄、锁状态标志、 线程持有的锁、偏向线程ID、偏向时间戳等等占用内存大小与虚拟机位长一致。Mark Word对应的类 型是 markOop 。源码位于 markOop.hpp 中。
<img src="https://img.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/0024.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0025.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0025.png">
在64位虚拟机下Mark Word是64bit大小的其存储结构如下
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0026.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0026.png">
在32位虚拟机下Mark Word是32bit大小的其存储结构如下
<img src="https://img.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/0027.png">
再加一个图对比一下,有一丁点的补充
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0028.png">
<img src="https://npm.elemecdn.com/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`
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0029.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0029.png">
@@ -571,7 +571,7 @@ synchronized(obj){
}
```
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0030.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0030.png">
## 轻量级锁什么时候升级为重量级锁?
@@ -608,7 +608,7 @@ synchronized(obj){
- 重量级锁的状态下,对象的`mark word`为指向一个堆中monitor对象的指针。一个monitor对象包括这么几个关键字段cxq下图中的ContentionListEntryList WaitSetowner。其中cxq EntryList WaitSet都是由ObjectWaiter的链表结构owner指向持有锁的线程。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0031.png">
<img src="https://npm.elemecdn.com/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主 要数据结构如下:
@@ -661,7 +661,7 @@ ObjectMonitor() {
1、执行monitorenter时会调用InterpreterRuntime.cpp (位于src/share/vm/interpreter/interpreterRuntime.cpp) 的 InterpreterRuntime::monitorenter函 数。具体代码可参见HotSpot源码。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0032.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0032.png">
@@ -1147,7 +1147,7 @@ if (TryLock(Self) > 0) break ;
可以看到ObjectMonitor的函数调用中会涉及到Atomic::cmpxchg_ptrAtomic::inc_ptr等内核函数 执行同步代码块没有竞争到锁的对象会park()被挂起竞争到锁的线程会unpark()唤醒。这个时候就 会存在操作系统用户态和内核态的转换这种切换会消耗大量的系统资源。所以synchronized是Java语 言中是一个重量级(Heavyweight)的操作。 用户态和和内核态是什么东西呢要想了解用户态和内核态还需要先了解一下Linux系统的体系架构
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0033.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0033.png">
从上图可以看出Linux操作系统的体系架构分为用户空间应用程序的活动空间和内核。 内核:本质上可以理解为一种软件,控制计算机的硬件资源,并提供上层应用程序运行的环境。 用户空间上层应用程序活动的空间。应用程序的执行必须依托于内核提供的资源包括CPU资源、存 储资源、I/O资源等。 系统调用:为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。

View File

@@ -410,11 +410,11 @@ Process finished with exit code 0
**技术翻译:**是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石 通过内置的FIFO队列来完成资源获取线程的排队工作并通过一个int类变量`state`表示持有锁的状态。
<img src="https://img.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/0001.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0011.png">
AbstractOwnableSynchronizer
AbstractQueuedLongSynchronizer
@@ -426,7 +426,7 @@ AbstractQueuedSynchronizer
AQS是一个抽象的父类可以将其理解为一个框架。基于AQS这个框架我们可以实现多种同步器比如下方图中的几个Java内置的同步器。同时我们也可以基于AQS框架实现我们自己的同步器以满足不同的业务场景需求。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0002.png">
@@ -434,7 +434,7 @@ AQS是一个抽象的父类可以将其理解为一个框架。基于AQS这
加锁会导致阻塞:有阻塞就需要排队,实现排队必然需要有某种形式的队列来进行管理
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0003.png">
1、抢到资源的线程直接使用办理业务抢占不到资源的线程的必然涉及一种**排队等候机制**,抢占资源失败的线程继续去等待(类似办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。
@@ -515,7 +515,7 @@ Node 的数据结构其实也挺简单的,就是 thread + waitStatus + pre + n
## AQS队列基本结构
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0004.png">
注意排队队列不包括head也就是后文要说的哨兵节点
@@ -583,7 +583,7 @@ public class AQSDemo {
以这样的一个实际例子说明。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0005.png">
@@ -765,7 +765,7 @@ public class AQSDemo {
2、C在if逻辑里准备入队进行相应设置后变成下面这样。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0006.png">
@@ -831,7 +831,7 @@ public class AQSDemo {
此时队列变成了下面的样子:
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0007.png">
3、然后if结束之后继续空的for循环B线程开始了第二轮循环。
@@ -843,11 +843,11 @@ public class AQSDemo {
2、`node.prev = t`进入if之后让B节点的prev指针指向t然后`compareAndSetTail(t, node)`设置尾节点
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0008.png">
3、CAS设置尾节点成功之后执行if里的逻辑
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0009.png">
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0010.png">

View File

@@ -33,7 +33,7 @@ date: 2021-04-08 14:21:58
相对简单的一个体系图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
## Netty 的应用场景
@@ -67,7 +67,7 @@ date: 2021-04-08 14:21:58
## Netty 的学习资料参考
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
<img src="https://npm.elemecdn.com/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`
3. `Java BIO`:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
4. `Java NIO`:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 `I/O` 请求就进行处理。【简单示意图】
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
5. `Java AIO(NIO.2)`:异步非阻塞,`AIO` 引入异步通道的概念,采用了 `Proactor` 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
6. 我们依次展开讲解。
@@ -120,7 +120,7 @@ date: 2021-04-08 14:21:58
## Java BIO 工作机制
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
`BIO` 编程流程的梳理
@@ -207,7 +207,7 @@ public class BIOServer {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
@@ -277,7 +277,7 @@ public class BasicBuffer {
3. `BIO` 基于字节流和字符流进行操作,而 `NIO` 基于 `Channel`(通道)和 `Buffer`(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。`Selector`(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
4. Buffer和Channel之间的数据流向是双向的
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
## NIO 三大核心原理示意图
@@ -287,7 +287,7 @@ public class BasicBuffer {
关系图的说明:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
1. 每个 `Channel` 都会对应一个 `Buffer`
2. `Selector` 对应一个线程,一个线程对应多个 `Channel`(连接)。
@@ -303,17 +303,17 @@ public class BasicBuffer {
缓冲区(`Buffer`):缓冲区本质上是一个**可以读写数据的内存块**,可以理解成是一个**容器对象(含数组)**,该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。`Channel` 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 `Buffer`,如图:【后面举例说明】
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
### Buffer 类及其子类
1.`NIO` 中,`Buffer` 是一个顶层父类,它是一个抽象类,类的层级关系图:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
2. `Buffer` 类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
@@ -321,13 +321,13 @@ public class BasicBuffer {
3. `Buffer` 类相关方法一览
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
### ByteBuffer
从前面可以看出对于 `Java` 中的基本数据类型(`boolean` 除外),都有一个 `Buffer` 类型与之相对应,最常用的自然是 `ByteBuffer` 类(二进制数据),该类的主要方法如下:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
## 通道Channel
@@ -343,7 +343,7 @@ public class BasicBuffer {
5. `FileChannel` 用于文件的数据读写,`DatagramChannel` 用于 `UDP` 的数据读写,`ServerSocketChannel``SocketChannel` 用于 `TCP` 的数据读写。
6. 图示
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
### FileChannel 类
@@ -444,7 +444,7 @@ public class NIOFileChannel02 {
2. 拷贝一个文本文件 `1.txt`,放在项目下即可
3. 代码演示
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
```java
package com.atguigu.nio;
@@ -721,7 +721,7 @@ public class ScatteringAndGatheringTest {
### Selector 示意图和特点说明
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
说明如下:
@@ -733,7 +733,7 @@ public class ScatteringAndGatheringTest {
### Selector 类相关方法
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
### 注意事项
@@ -748,7 +748,7 @@ public class ScatteringAndGatheringTest {
`NIO` 非阻塞网络编程相关的(`Selector``SelectionKey``ServerScoketChannel``SocketChannel`)关系梳理图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
对上图的说明:
@@ -938,21 +938,21 @@ public static final int OP_ACCEPT = 1 << 4;
2. `SelectionKey` 相关方法
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
### ServerSocketChannel
1. `ServerSocketChannel` 在服务器端监听新的客户端 `Socket` 连接,负责监听,不负责实际的读写操作
2. 相关方法如下
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
### SocketChannel
1. `SocketChannel`,网络 `IO` 通道,**具体负责进行读写操作**。`NIO` 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
2. 相关方法如下
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
## NIO网络编程应用实例 - 群聊系统
@@ -965,7 +965,7 @@ public static final int OP_ACCEPT = 1 << 4;
5. 目的:进一步理解 `NIO` 非阻塞网络编程机制
6. 示意图分析和代码
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
代码:
@@ -1243,7 +1243,7 @@ socket.getOutputStream().write(arr);
### 传统 IO 模型
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
**DMA**`direct memory access` 直接内存拷贝(不使用 `CPU`
@@ -1252,19 +1252,19 @@ socket.getOutputStream().write(arr);
1. `mmap` 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图
2. `mmap` 示意图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
### sendFile 优化
1. `Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`,同时,由于和用户态完全无关,就减少了一次上下文切换
2. 示意图和小结
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
3. 提示:零拷贝从操作系统角度,是没有 `cpu` 拷贝
4. `Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图和小结:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
5. 这里其实有一次 `cpu` 拷贝 `kernel buffer` -> `socket buffer` 但是,拷贝的信息很少,比如 `lenght``offset` 消耗低,可以忽略

View File

@@ -25,7 +25,7 @@ date: 2021-04-21 17:38:58
1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
2. `codec`(编解码器)的组成部分有两个:`decoder`(解码器)和 `encoder`(编码器)。`encoder` 负责把业务数据转换成字节码数据,`decoder` 负责把字节码数据转换成业务数据
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
## Netty 本身的编码解码的机制和问题分析
@@ -54,7 +54,7 @@ date: 2021-04-21 17:38:58
8. 然后通过 `protoc.exe` 编译器根据 `.proto` 自动生成 `.java` 文件
9. `protobuf` 使用示意图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
## Protobuf 快速入门实例
@@ -91,7 +91,7 @@ message Student { //会在 StudentPOJO 外部类生成一个内部类 Student
protoc.exe --java_out=.Student.proto
将生成的 StudentPOJO 放入到项目使用
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
生成的StudentPOJO代码太长就不贴在这里了
@@ -676,7 +676,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
2. `ChannelHandler` 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 `ChannelInboundHandler` 接口(或 `ChannelInboundHandlerAdapter`),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 `ChannelInboundHandler` 冲刷数据。业务逻辑通常写在一个或者多个 `ChannelInboundHandler` 中。`ChannelOutboundHandler` 原理一样,只不过它是用来处理出站数据的
3. `ChannelPipeline` 提供了 `ChannelHandler` 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 `pipeline` 中的一系列 `ChannelOutboundHandler`,并被这些 `Handler` 处理,反之则称为入站的
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
> 出站,入站如果搞不清楚,看下面的**Netty的handler链的调用机制**,通过一个例子和图讲清楚
@@ -689,12 +689,12 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
1. 关系继承图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
2. 由于不可能知道远程节点是否会一次性发送一个完整的信息,`tcp` 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.【后面有说TCP的粘包和拆包问题】
3. 一个关于 `ByteToMessageDecoder` 实例分析
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
@@ -710,7 +710,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
> 读者可以看下这个图,带着这个图去看下面的例子。
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
@@ -978,11 +978,11 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
### 效果
<img src="https://img.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/0008.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
@@ -1002,7 +1002,7 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
@@ -1090,13 +1090,13 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
如下图验证结果:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
2. 同时又引出了一个小问题
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
当我们`MyClientHandler`传一个Long时会调用我们的`MyLongToByteEncoder`的编码器。那么控制台就会打印这样一句话:**MyLongToByteEncoder encode 被调用**。但是这里并没有调用编码器,这是为什么呢?
@@ -1149,7 +1149,7 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd",CharsetUtil.UTF_8));
```
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
@@ -1198,7 +1198,7 @@ public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> {
## 其它编解码器
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
@@ -1249,7 +1249,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
3. 演示整合
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
@@ -1261,7 +1261,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
2. 由于 `TCP` 无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题,看一张图
3. `TCP` 粘包、拆包图解
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
假设客户端分别发送了两个数据包 `D1``D2` 给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:
@@ -1502,11 +1502,11 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
**Client**
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
**Server**
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
@@ -1514,13 +1514,13 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
**Client**
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
**Server**
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
@@ -1538,7 +1538,7 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
1. 要求客户端发送 `5``Message` 对象,客户端每次发送一个 `Message` 对象
2. 服务器端每次接收一个 `Message`,分 `5` 次进行解码,每读取到一个 `Message`,会回复一个 `Message` 对象给客户端。
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
@@ -1996,7 +1996,7 @@ MyMessageEncoder encode 方法被调用
1. `RPCRemote Procedure Call`—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
2. 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样(如图)
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
过程:
@@ -2018,11 +2018,11 @@ MyMessageEncoder encode 方法被调用
3. 常见的 `RPC` 框架有:比较知名的如阿里的 `Dubbo``Google``gRPC``Go` 语言的 `rpcx``Apache``thrift``Spring` 旗下的 `SpringCloud`
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
## 我们的RPC 调用流程图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
**RPC 调用流程说明**
@@ -2052,7 +2052,7 @@ MyMessageEncoder encode 方法被调用
3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 `Netty` 请求提供者返回数据
4. 开发的分析图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />

View File

@@ -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.
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
## Netty 的优点
@@ -80,7 +80,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
1. 当并发数很大,就会创建大量的线程,占用很大系统资源
2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的`read` 操作,导致上面的处理线程资源浪费
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
## Reactor 模式
@@ -97,7 +97,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
1. 基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。(解决了当并发数很大时,会创建大量线程,占用很大系统资源)
2. 基于 `I/O` 复用模型:多个客户端进行连接,先把连接请求给`ServiceHandler`。多个连接共用一个阻塞对象`ServiceHandler`。假设当C1连接没有数据要处理时C1客户端只需要阻塞于`ServiceHandler`C1之前的处理线程便可以处理其他有数据的连接不会造成线程资源的浪费。当C1连接再次有数据时`ServiceHandler`根据线程池的空闲状态将请求分发给空闲的线程来处理C1连接的任务。解决了线程资源浪费的那个问题
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
<img src="https://npm.elemecdn.com/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 模式基本设计思想,如图
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
<img src="https://npm.elemecdn.com/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` 群聊系统验证
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
<img src="https://npm.elemecdn.com/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` 在多线程中运行
<img src="https://img.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/0007.png" />
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
> 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 的原理图解
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
<img src="https://npm.elemecdn.com/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`
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
<img src="https://npm.elemecdn.com/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 - 进阶版
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
<img src="https://npm.elemecdn.com/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 - 详细版
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
@@ -669,9 +669,9 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
下面第一张图就是管道中间会经过多个handler
<img src="https://img.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/0013.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
说明:
@@ -917,11 +917,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
2. `ChannelHandler` 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
3. `ChannelHandler` 及其实现类一览图(后)
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
4. 我们经常需要自定义一个 `Handler` 类去继承 `ChannelInboundHandlerAdapter`,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
## Pipeline 和 ChannelPipeline
@@ -931,7 +931,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
2. `ChannelPipeline` 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 `Channel` 中各个的 `ChannelHandler` 如何相互交互
3.`Netty` 中每个 `Channel` 都有且仅有一个 `ChannelPipeline` 与之对应,它们的组成关系如下
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
4. 常用方法
`ChannelPipeline addFirst(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的第一个位置`ChannelPipeline addLast(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的最后一个位置
@@ -940,7 +940,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
从head看一下debug
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
- `TestServerInitializer``HttpServerCodec`这些东西本身也是`handler`
- 一般来说事件从客户端往服务器走我们称为出站,反之则是入站。
@@ -950,7 +950,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
1. 保存 `Channel` 相关的所有上下文信息,同时关联一个 `ChannelHandler` 对象
2.`ChannelHandlerContext` 中包含一个具体的事件处理器 `ChannelHandler`,同时 `ChannelHandlerContext` 中也绑定了对应的 `pipeline``Channel` 的信息,方便对 `ChannelHandler` 进行调用。
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
3. 常用方法
@@ -959,14 +959,14 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
- `ChannelFuture writeAndFlush(Object msg)`,将数据写到
- `ChannelPipeline` 中当前 `ChannelHandler` 的下一个 `ChannelHandler` 开始处理(出站)
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
## ChannelOption
1. `Netty` 在创建 `Channel` 实例后,一般都需要设置 `ChannelOption` 参数。
2. `ChannelOption` 参数如下:
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
## EventLoopGroup 和其实现类 NioEventLoopGroup
@@ -974,7 +974,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
2. `EventLoopGroup` 提供 `next` 接口,可以从组里面按照一定规则获取其中一个 `EventLoop` 来处理任务。在 `Netty` 服务器端编程中,我们一般都需要提供两个 `EventLoopGroup`,例如:`BossEventLoopGroup``WorkerEventLoopGroup`
3. 通常一个服务端口即一个 `ServerSocketChannel` 对应一个 `Selector` 和一个 `EventLoop` 线程。`BossEventLoop` 负责接收客户端的连接并将 `SocketChannel` 交给 `WorkerEventLoopGroup` 来进行 `IO` 处理,如下图所示
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
4. 常用方法
`public NioEventLoopGroup()`,构造方法
@@ -985,11 +985,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
1. `Netty` 提供一个专门用来操作缓冲区(即 `Netty` 的数据容器)的工具类
2. 常用方法如下所示
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
3. 举例说明 `Unpooled` 获取 `Netty` 的数据容器 `ByteBuf` 的基本使用
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
案例 1
@@ -1096,7 +1096,7 @@ public class NettyByteBuf02 {
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
代码如下:
@@ -1523,7 +1523,7 @@ public class MyServerHandler extends ChannelInboundHandlerAdapter {
4. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知
5. 运行界面
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
@@ -1724,7 +1724,7 @@ public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<Tex
可以看到并不是发一次数据,连接就关闭了,而是可以继续发送。
<img src="https://img.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>

View File

@@ -47,7 +47,7 @@ date: 2021-04-08 15:21:58
3. 而在用户进程这边,整 个进程会被阻塞。当**内核**一直等到数据准备好了,它就会将数据从**内核**中拷贝到用户内存,然后**内核**返回果,用户进程才解除 block的状态重新运行起来。
4. **所以blocking IO的特点就是在IO执行的两个阶段都被block了。**
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0003.png">
<img src="https://npm.elemecdn.com/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这一特性。
4. **所以,用户进程第一个阶段不是阻塞的,需要不断的主动询问内核数据好了没有;第二个阶段依然总是阻塞的。**
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0004.png">
<img src="https://npm.elemecdn.com/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请求。
3. 从流程上来看使用select函数进行IO请求和同步阻塞模型没有太大的区别甚至还多了添加监视socket以及调用select函数的额外操作效率更差。但是使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket然后不断地调用select读取被激活的socket(也就是数据准备好了的socket)即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中必须通过多线程的方式才能达到这个目的。
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0005.png">
**select函数**
@@ -90,7 +90,7 @@ date: 2021-04-08 15:21:58
4. 通过Reactor的方式可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作异步而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时(就是数据准备好的时候)则通知相应的用户线程或执行用户线程的回调函数执行handle_event进行数据读取、处理的工作。
5. 由于select函数是阻塞的因此多路IO复用模型也被称为异步阻塞IO模型。注意这里的所说的阻塞是指select函数执行时线程被阻塞而不是指socket。(一般在使用IO多路复用模型时socket都是设置为NONBLOCK的不过这并不会产生影响因为用户发起IO请求时数据已经到达了用户线程一定不会被阻塞。)
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0001.png">
@@ -106,7 +106,7 @@ date: 2021-04-08 15:21:58
3. 而另一方面,从**内核**的角度,当它受到一个异步读之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都 完成之后,**内核**会给用户进程发送一个信号告诉它read操作完成了用户线程直接使用即可。 在这整个过程中,进程完全没有被阻塞。
4. 异步IO模型使用了Proactor设计模式实现了这一机制。**(具体怎么搞得,看上面的文章链接)**
<img src="https://img.imlql.cn/youthlql@1.0.8/computer_network/summary/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/computer_network/summary/0007.png">
@@ -196,7 +196,7 @@ date: 2021-04-08 15:21:58
1. 来看一个标准设备不是真实存在的相当于一个逻辑上抽象的东西通过它来帮助我们更好地理解设备交互的机制。可以看到一个包含两部分重要组件的设备。第一部分是向系统其他部分展现的硬件接口interface。同软件一样硬件也需要一些接口让系统软件来控制它的操作。因此所有设备都有自己的特定接口以及典型交互的协议。
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0002.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0002.png"/>
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又可以继续运行。
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0003.png"/>
2. 有了中断后中断允许计算与I/O重叠overlap这是提高CPU利用率的关键。我们利用中断并允许重叠操作系统就可以在等待磁盘操作时做其他事情。
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0004.png"/>
- 在这个例子中在磁盘处理进程1的请求时操作系统在CPU上运行进程2。磁盘处理完成后触发一个中断然后操作系统唤醒进程1继续运行。这样在这段时间无论CPU还是磁盘都可以有效地利用。
@@ -243,7 +243,7 @@ While (STATUS == BUSY);//wait until device is done with your request
中断仍旧存在的缺点:
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0005.png"/>
IO过程简述
@@ -268,7 +268,7 @@ IO过程简述
>
> 标准协议还有一点需要我们注意。具体来说如果使用编程的I/O将一大块数据传给设备CPU又会因为琐碎的任务而变得负载很重浪费了时间和算力本来更好是用于运行其他进程。下面的时间线展示了这个问题
>
> <img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0006.png"/>
> <img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0006.png"/>
>
> 进程1在运行过程中需要向磁盘写一些数据所以它开始进行I/O操作将数据从内存拷贝到磁盘其中标示c的过程。**拷贝结束后磁盘上的I/O操作开始执行此时CPU才可以处理其他请求。**
@@ -278,13 +278,13 @@ IO过程简述
>
> DMA工作过程如下。为了能够将数据传送给设备操作系统会通过编程告诉DMA引擎数据在内存的位置要拷贝的大小以及要拷贝到哪个设备。在此之后操作系统就可以处理其他请求了。当DMA的任务完成后DMA控制器会抛出一个中断来告诉操作系统自己已经完成数据传输。修改后的时间线如下
>
> <img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0007.png"/>
> <img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0007.png"/>
>
> 从时间线中可以看到数据的拷贝工作都是由DMA控制器来完成的。因为CPU在此时是空闲的所以操作系统可以让它做一些其他事情比如此处调度进程2到CPU来运行。因此进程2在进程1再次运行之前可以使用更多的CPU。
为了更好理解,看图:
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0008.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0008.png"/>
过程:
@@ -302,7 +302,7 @@ IO过程简述
场景:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0009.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0009.png"/>
1. 很明显发生了4次拷贝
@@ -326,7 +326,7 @@ IO过程简述
`read()` 系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,为了减少这一步开销,我们可以用 `mmap()` 替换 `read()` 系统调用函数。`mmap()` 系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样,操作系统内核与用户空间共享缓冲区,就不需要再进行任何的数据拷贝操作。
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0010.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0010.png"/>
总的来说mmap减少了一次数据拷贝总共4次上下文切换3次数据拷贝
@@ -336,7 +336,7 @@ IO过程简述
`Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0011.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0011.png"/>
总的来说有2次上下文切换3次数据拷贝。
@@ -344,7 +344,7 @@ IO过程简述
`Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝
<img src="https://img.imlql.cn/youthlql@1.0.0/os/IO_and_Zero-copy/0012.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/os/IO_and_Zero-copy/0012.png" />

View File

@@ -38,7 +38,7 @@ Spring源码纵览这一节主要是先了解下Spring的一些核心东西
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926195844253.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926195844253.png" />
- **蓝色实线箭头**是指继承关系
- **绿色虚线箭头**是指接口实现关系
@@ -91,13 +91,13 @@ BeanPostProcessor
快捷键ctrl+F12 看类的方法
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926232957909.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926232957909.png"/>
#### 实现类
快捷键ctrl+h 查看接口实现类
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926233308861.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926233308861.png"/>
@@ -189,7 +189,7 @@ public interface ResourceLoader {
#### 实现类
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926234527992.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210926234527992.png" />
@@ -232,7 +232,7 @@ public interface BeanFactory {
> 源码分析小技巧:看源码时,我们可以先看一个类的接口继承关系,因为接口就是规范,大部分开源框架源码都是遵守这一规范的。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927103844753.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927103844753.png" />
@@ -248,7 +248,7 @@ public interface BeanFactory {
#### AbstractApplicationContext
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927104922679.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927104922679.png" />
环境类的意思就是谁持有这个策略这里就解释了上文说ResourceLoader是环境类接口
@@ -265,7 +265,7 @@ public interface BeanFactory {
#### GenericApplicationContext
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927105641446.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927105641446.png"/>
这里组合了DefaultListableBeanFactory
@@ -310,7 +310,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927110806180.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927110806180.png" />
1. BeanDefinitionRegistryBean定义信息注册中心
2. SimpleAliasRegistry别名注册中心
@@ -336,7 +336,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
#### 流程图-BeanDefinition注册流程
<img src="https://img.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">
<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">
- 我们要看BeanDefinition是何时被放入到beanDefinitionMap只需要在DefaultListableBeanFactory用到`beanDefinitionMap.put()`的地方打个断点。
- 我们在DefaultListableBeanFactory里搜索发现了registerBeanDefinition注册Bean定义信息这个方法名很像我们要找的东西再看里面的代码果然有`beanDefinitionMap.put()`这串代码,我们试着在这里打个断点
@@ -361,7 +361,7 @@ public class MainTest {
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927142317864.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927142317864.png" />
> 调用栈的调用顺序已经非常清楚了,可以把图放大一点看,下面只说一些必要的信息。
@@ -780,7 +780,7 @@ public class MainTest {
- 还有一个debug的猜测方向想要注册BeanDefinition肯定要new我们可以直接在AbstractBeanDefinition这个抽象父类的构造函数打断点我们不知道会走哪个构造函数所以给三个构造函数都打断点。
- 下面就是打完断点之后运行MainTest测试类后的调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927172855410.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927172855410.png"/>
@@ -936,7 +936,7 @@ public class MainTest {
### ApplicationContext接口功能
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927175207106.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927175207106.png" />
1. ioc事件派发器
2. 国际化解析
@@ -969,7 +969,7 @@ public interface Aware {
2. 注释的大致意思是Aware是一个标记性的超接口顶级接口指示了一个Bean有资格通过回调方法的形式获取Spring容器底层组件。实际回调方法被定义在每一个子接口中而且通常一个子接口只包含一个接口一个参数并且返回值为void的方法。
3. 说白了只要实现了Aware子接口的Bean都能获取到一个Spring底层组件。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927191112023.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927191112023.png" />
比如实现了ApplicationContextAware接口实现它的方法就能通过回调机制拿到ApplicationContext
@@ -979,7 +979,7 @@ public interface Aware {
##### 流程图-Bean对象创建流程
<img src="https://img.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">
<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">
##### Debug调用栈
@@ -1055,7 +1055,7 @@ public class MainConfig {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927200211334.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927200211334.png" />
1. 有一些一样的东西不再赘述
2.`AbstractApplicationContext#refresh()`里会调用
@@ -1267,7 +1267,7 @@ public class MainConfig {
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927230734973.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210927230734973.png"/>
@@ -1434,7 +1434,7 @@ public class MainTest {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928113235852.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928113235852.png" />
@@ -1475,7 +1475,7 @@ public class MainTest {
我们看到此时Person的name属性还是null
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928114746618.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928114746618.png" />
@@ -1497,7 +1497,7 @@ public class MainTest {
这里拿到属性值
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115110820.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115110820.png" />
@@ -1512,13 +1512,13 @@ public class MainTest {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115352416.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115352416.png"/>
bw就是上面说的 => 里面封装好了真正的Person对象
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115702735.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928115702735.png"/>
@@ -1528,7 +1528,7 @@ bw就是上面说的 => 里面封装好了真正的Person对象
这里就是一层一层调,不重要跳过。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928120016581.png" />
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928141536440.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928141536440.png"/>
@@ -1588,7 +1588,7 @@ private class BeanPropertyHandler extends PropertyHandler {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143301161.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143301161.png"/>
最后就是反射走到了我们的`Person#setName(String name)`
@@ -1598,7 +1598,7 @@ private class BeanPropertyHandler extends PropertyHandler {
### 再来看看messageSource何时赋值
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928143910461.png" />
<img src="https://npm.elemecdn.com/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://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928151825366.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928151825366.png"/>
### AbstractAutowireCapableBeanFactory#populateBean()
@@ -1785,7 +1785,7 @@ public class Person implements ApplicationContextAware, MessageSourceAware {
这里有一个非常著名的后置处理器,`AutowiredAnnotationBeanPostProcessor`自动装配注解后置处理器,顾名思义就是用来处理@Autowired注解自动装配的
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928153428691.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_01/image-20210928153428691.png" />

View File

@@ -97,7 +97,7 @@ public interface BeanFactoryPostProcessor {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928171641029.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928171641029.png"/>
核心就是我们对于传进来的参数,可以**修改,覆盖,添加**它的东西。对于BeanPostProcessor来说传进来的参数是`(Object bean, String beanName)` 它都已经把bean传给你了这意味着我们可以修改传进来的Bean的任何东西。不管你是事务也好AOP也好都是通过这些个后置处理器来添加这些额外功能的。
@@ -106,11 +106,11 @@ BeanFactoryPostProcessor后置增强BeanFactory也就是增强Bean工厂
### BeanFactoryPostProcessor的接口关系
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928172716846.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928172716846.png" />
### BeanPostProcessor接口关系
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928173012384.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928173012384.png"/>
DestructionAwareBeanPostProcessor接口是跟销毁有关的我们这里不分析
@@ -391,7 +391,7 @@ public class MainTest {
### 流程图-Bean生命周期与后置工厂处理器
<img src="https://img.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E5%90%8E%E7%BD%AE%E5%B7%A5%E5%8E%82%E5%A4%84%E7%90%86%E5%99%A8.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E5%90%8E%E7%BD%AE%E5%B7%A5%E5%8E%82%E5%A4%84%E7%90%86%E5%99%A8.png">
### BeanDefinitionRegistryPostProcessor
@@ -399,7 +399,7 @@ public class MainTest {
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928193241982.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928193241982.png"/>
##### AbstractApplicationContext#refresh()
@@ -615,9 +615,9 @@ public class MainTest {
> Spring中所有组件的获取都是通过getBean(),容器中有就拿,没有就创建。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928203211130.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928203211130.png" />
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928195709384.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928195709384.png" />
下面那个是Spring默认提供的后置处理器我们后面再讲。
@@ -677,11 +677,11 @@ public interface Ordered {
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928203957687.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928203957687.png"/>
从PostProcessorRegistrationDelegate 142行开始走不同的调用代码在上面有注释
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928204229225.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928204229225.png" />
##### PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors()
@@ -706,7 +706,7 @@ private static void invokeBeanDefinitionRegistryPostProcessors(
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928204806052.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928204806052.png" />
@@ -733,7 +733,7 @@ private static void invokeBeanFactoryPostProcessors(
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928210240702.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928210240702.png" />
@@ -741,7 +741,7 @@ private static void invokeBeanFactoryPostProcessors(
##### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928210858103.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928210858103.png">
代码注释也是上面那个BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor执行逻辑基本一样
@@ -779,11 +779,11 @@ public class MainConfig {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928211847405.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928211847405.png" />
从这一步进来
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212026255.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212026255.png" />
@@ -791,7 +791,7 @@ public class MainConfig {
#### PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors()
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212134359.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212134359.png" />
F7进入
@@ -799,7 +799,7 @@ F7进入
ConfigurationClassPostProcessor配置类的后置处理
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212332382.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212332382.png" />
@@ -914,7 +914,7 @@ ConfigurationClassPostProcessor配置类的后置处理
这几个怎么来的我们后面说
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212543963.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_02/image-20210928212543963.png" />

View File

@@ -250,7 +250,7 @@ public class MainTest {
### 流程图-Bean生命周期与后置处理器
<img src="https://img.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E5%90%8E%E7%BD%AE%E5%A4%84%E7%90%86%E5%99%A8.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/Bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E5%90%8E%E7%BD%AE%E5%A4%84%E7%90%86%E5%99%A8.png">
### BeanPostProcessor-执行无参构造
@@ -258,7 +258,7 @@ public class MainTest {
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929142257774.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929142257774.png"/>
#### AbstractApplicationContext#registerBeanPostProcessors()注册Bean后置处理器
@@ -345,7 +345,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
- Bean工厂后置处理器调用的是`invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory , List<BeanFactoryPostProcessor> )`
- Bean后置处理器调用的是`registerBeanPostProcessors(ConfigurableListableBeanFactory , AbstractApplicationContext )`
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929144115434.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929144115434.png"/>
@@ -355,7 +355,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
> 2. BeanFactoryPostProcessor是先执行完每一个的无参构造和实现的几个方法再去执行下一个BeanFactoryPostProcessor
> 3. BeanPostProcessor是先执行所有BeanPostProcessor的无参构造再执行所有BeanPostProcessor实现的方法。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929145612361.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929145612361.png"/>
### MergedBeanDefinitionPostProcessor-执行无参构造
@@ -365,7 +365,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
同上
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929150155334.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929150155334.png"/>
@@ -375,7 +375,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929160714133.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929160714133.png"/>
@@ -542,7 +542,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
3. 判断是不是想要的类型。
3. 这里有没有优化空间再存一个BeanType=>BeanName的对应关系但是这样的关系是一对多的同一个BeanType下可能有多个beanNameSpring可能是考虑到空间成本没有这样弄。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929163852056.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929163852056.png"/>
@@ -554,7 +554,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
2. 我们来看看此时单例池里有哪些对象
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929170557025.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929170557025.png" />
```java
protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
@@ -687,13 +687,13 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
在此方法里为什么Cat会进来两次呢往后面看
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929172115093.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929172115093.png" />
### InstantiationAwareBeanPostProcessor-执行postProcessBeforeInstantiation方法
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929172554306.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929172554306.png"/>
#### AbstractApplicationContext#finishBeanFactoryInitialization()完成BeanFactory初始化
@@ -746,7 +746,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
2. 上面我们也看了只有Cat的对象还没创建还没初始化所以下面就开始创建对象了。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929173103459.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929173103459.png" />
@@ -860,7 +860,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929174424136.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929174424136.png"/>
> 前面还是一样的执行逻辑,直接来到下面
@@ -1003,7 +1003,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
1. 实例提供者:
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929175511913.png" >
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210929175511913.png" >
@@ -1025,7 +1025,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
### Cat-执行无参构造方法
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930110158555.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930110158555.png" />
@@ -1039,7 +1039,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930110913527.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930110913527.png" />
@@ -1061,7 +1061,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930112247246.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930112247246.png"/>
#### AbstractAutowireCapableBeanFactory#populateBean()属性赋值
@@ -1141,7 +1141,7 @@ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFa
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930112621989.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930112621989.png"/>
@@ -1193,7 +1193,7 @@ public void setValue(@Nullable Object value) throws Exception { //name setName
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930113459713.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930113459713.png"/>
@@ -1201,7 +1201,7 @@ public void setValue(@Nullable Object value) throws Exception { //name setName
#### AutowiredAnnotationBeanPostProcessor#postProcessProperties()
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930114234547.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930114234547.png" />
```java
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
@@ -1322,7 +1322,7 @@ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, Str
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930114612277.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930114612277.png"/>
这也说明了,@Autowire,@Value赋值的时候会去找setXXX,这也是@Autowire的原理
@@ -1350,7 +1350,7 @@ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, Str
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930143504497.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930143504497.png"/>
@@ -1461,7 +1461,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930150311019.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930150311019.png"/>
@@ -1513,7 +1513,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
#### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930150604716.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_03/image-20210930150604716.png"/>

View File

@@ -16,7 +16,7 @@ date: 2022-01-27 21:01:02
## 流程图-bean初始化流程
<img src="https://img.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/bean%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/bean%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.jpg">
## AbstractApplicationContext#refresh()
@@ -206,7 +206,7 @@ date: 2022-01-27 21:01:02
## 工厂Bean的初始化方式
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930171319531.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930171319531.png"/>
@@ -313,13 +313,13 @@ public class Hello {
从这里开始看调用链
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930175345542.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930175345542.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930175539928.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930175539928.png"/>
@@ -475,13 +475,13 @@ String FACTORY_BEAN_PREFIX = "&";
最后返回
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930180811270.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930180811270.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930181219048.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20210930181219048.png"/>
@@ -532,7 +532,7 @@ public class Cat implements InitializingBean, SmartInitializingSingleton {
### Debug调用栈
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20211008174534713.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_04/image-20211008174534713.png"/>

View File

@@ -18,7 +18,7 @@ date: 2022-02-13 18:01:02
## 流程图-容器刷新
<img src="https://img.imlql.cn/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/%E5%AE%B9%E5%99%A8%E5%88%B7%E6%96%B0%E6%B5%81%E7%A8%8B.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.7/spring-sourcecode-v1/flow_chart/%E5%AE%B9%E5%99%A8%E5%88%B7%E6%96%B0%E6%B5%81%E7%A8%8B.jpg">
## 容器创建
@@ -209,7 +209,7 @@ public class AnnotationMainTest {
上面的方法走完我们可以看看到主要是下面4个后置处理器
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211008212541000.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211008212541000.png"/>
##### RootBeanDefinition
@@ -360,7 +360,7 @@ public class AnnotationMainTest {
走完之后,注册中心肯定多了咱们的配置类
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211008213900796.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211008213900796.png"/>
@@ -584,7 +584,7 @@ protected void initPropertySources() {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009105903816.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009105903816.png"/>
@@ -607,11 +607,11 @@ protected void initPropertySources() {
这一步有个很关键的后置处理器
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009113419863.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009113419863.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009114151034.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009114151034.png"/>
@@ -746,7 +746,7 @@ protected void initPropertySources() {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009115044348.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009115044348.png"/>
@@ -1043,7 +1043,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
拿到所有类(资源),不管你有没有标@Component注解。然后挨个遍历每一个资源是不是候选的组件(根据前面准备的一些条件,在这里进行判断)
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009145119146.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009145119146.png"/>
@@ -1051,7 +1051,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
最后我们看一下执行完之后的BeanDefinition信息
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009150338205.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009150338205.png"/>
@@ -1309,7 +1309,7 @@ public class B {
这就是循环引用的场景这种写法由于Spring内部获取Bean都是通过getBean方法来获取就造成了下面的死循环。我们来看看Spring是怎么解决的。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20210928203211130.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20210928203211130.png">
@@ -1568,16 +1568,16 @@ public class B {
1. pos_1位置先进入pos_3位置的`getSingleton(beanName, true)`查看缓存中有没有A组件
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009165057518.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009165057518.png"/>
2. 然后走到pos_2调用pos_5的`getSingleton()`开始创建A的流程
3. 在pos_5的`getSingleton()`中走到pos_6的`beforeSingletonCreation()`,就变成下面这样
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009165924811.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009165924811.png"/>
4. 接着pos_7的会调用pos_2的lamda表达式里的`createbean()`,里面再调用`doCreateBean()`。前面讲过不多说最终调用A的无参构造(pos_8)创建完之后发现A的B属性是null。
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009170632290.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009170632290.png"/>
5.在pos_9处的`addSingletonFactory()`来准备解决循环引用
@@ -1594,7 +1594,7 @@ public class B {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009171641665.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009171641665.png"/>
@@ -1662,11 +1662,11 @@ public class B {
通过前面讲过的AutowiredAnnotationBeanPostProcessor来注入B最后发现要调用setB方法给B赋值
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009172602037.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009172602037.png"/>
7. 继续走发现要想获得B还是要调用getBean
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009172943987.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009172943987.png" />
```java
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
@@ -1680,13 +1680,13 @@ public class B {
然后图就是这样子
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009173310844.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009173310844.png"/>
8. B也是走这一套
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175223998.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175223998.png"/>
9. B为了获取A还要再走一次getBean()流程,最终还是走到
@@ -1720,13 +1720,13 @@ public class B {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175603012.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175603012.png"/>
10.
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175756620.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009175756620.png"/>
@@ -1739,7 +1739,7 @@ public class B {
12. B初始化完之后回到getSingleton把自己放到单例池里
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180127487.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180127487.png"/>
```java
protected void addSingleton(String beanName, Object singletonObject) {
@@ -1752,15 +1752,15 @@ protected void addSingleton(String beanName, Object singletonObject) {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180214917.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180214917.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180410898.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180410898.png"/>
13. B全部结束之后回到A的流程A赋值工作结束了然后就开始A的初始化。初始化的过程中
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180807630.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180807630.png"/>
```java
protected void addSingleton(String beanName, Object singletonObject) {
@@ -1775,7 +1775,7 @@ protected void addSingleton(String beanName, Object singletonObject) {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180545206.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/image-20211009180545206.png"/>
@@ -1787,7 +1787,7 @@ protected void addSingleton(String beanName, Object singletonObject) {
<img src="https://img.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/循环引用.jpg">
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_05/循环引用.jpg">

View File

@@ -435,7 +435,7 @@ public class JZ_056 {
}
```
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0001.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0001.png"/>
由上面代码可以看出来this()方法可以调用本类的无参构造函数。如果本类继承的有父类那么无参构造函数会有一个隐式的super()的存在。所以在同一个构造函数里面如果this()之后再调用super()那么就相当于调用了两次super(),也就是调用了两次父类的无参构造,就失去了语句的意义,编译器也不会通过。
@@ -525,7 +525,7 @@ https://blog.csdn.net/striner/article/details/86375684
2. 遍历链表或调用红黑树相关的删除方法
3. 从 LinkedHashMap 维护的双链表中移除要删除的节点
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0002.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0002.png" />
@@ -537,7 +537,7 @@ https://blog.csdn.net/qq_36610462/article/details/83277524
## 集合继承结构
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0010.jpeg">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0010.jpeg">
@@ -675,11 +675,11 @@ https://blog.csdn.net/puppylpg/article/details/80433271
这里我只截图下我准备的面试题目录因为所有答案你都能在笔者的JVM文章里找到答案
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0003.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0003.png"/>
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0004.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0004.png"/>
@@ -1335,7 +1335,7 @@ redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能
## 存储引擎Innodb和Myisam的区别以及使用场景
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0005.png"/>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0005.png"/>
@@ -2039,7 +2039,7 @@ snowflake 算法是 twitter 开源的分布式 id 生成算法,采用 Scala
## Bean生命周期
<img src= "https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0006.png">
<img src= "https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0006.png">
@@ -2199,7 +2199,7 @@ https://github.com/Snailclimb/springboot-guide/blob/master/docs/interview/spring
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0007.png">
@@ -2331,7 +2331,7 @@ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Ob
11、DispatcherServlet 类向用户返回响应。
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0008.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0008.png" />
@@ -2416,7 +2416,7 @@ https://blog.csdn.net/j04110414/article/details/78914787
<img src="https://img.imlql.cn/youthlql@1.0.0/xiaozhao/shuiwen/0009.png" >
<img src="https://npm.elemecdn.com/youthlql@1.0.0/xiaozhao/shuiwen/0009.png" >
从整个秒杀系统的架构其实和一般的互联网系统架构本身没有太多的不同,核心理念还是通过缓存、异步、限流来保证系统的高并发和高可用。下面从一笔秒杀交易的流程来描述下秒杀系统架构设计的要点: