1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[docs feat]vuepress主题更新

This commit is contained in:
guide 2022-03-03 09:14:56 +08:00
parent b7780513c5
commit 5a5f8ccb3b
63 changed files with 823 additions and 1360 deletions

11
.gitignore vendored
View File

@ -1,4 +1,13 @@
/node_modules
/package-lock.json
/dist
.DS_Store
# VS Code Config file
.vscode/
# Vuepress Cache
.cache/
# Vuepress Temp
.temp/
# Vuepress Output
dist/
# Build files
packages/*/lib/

View File

@ -16,22 +16,22 @@ Read in other languages: [Mandarin](https://github.com/Snailclimb/JavaGuide/blob
> 6. **Interview Special Edition** : For those who are preparing for the interview, you can consider the interview special edition: [Java Interview Advanced Guide].(https://www.yuque.com/docs/share/f37fc804-bfe6-4b0d-b373-9c462188fec7) (Very high quality, built specifically for interviews, free for planet users)
> 7. **Reprint Instructions**: All the following articles are my (Guide) original if not stated at the beginning of the text, reproduced at the beginning of the text to indicate the source, if found malicious plagiarism / transport, will use legal weapons to defend their rights. Let's maintain a good technical creation environment together! ⛽️
<p align="center">
<p style="text-align:center">
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
<img src="https://img-blog.csdnimg.cn/img_convert/1c00413c65d1995993bf2b0daf7b4f03.png#pic_center" width=""/>
</a>
</p>
<p align="center">
<p style="text-align:center">
<a href="https://javaguide.cn/"><img src="https://img.shields.io/badge/阅读-read-brightgreen.svg" alt="阅读"></a>
<img src="https://img.shields.io/github/stars/Snailclimb/JavaGuide" alt="stars"/>
<img src="https://img.shields.io/github/forks/Snailclimb/JavaGuide" alt="forks"/>
<img src="https://img.shields.io/github/issues/Snailclimb/JavaGuide" alt="issues"/>
</p>
<h3 align="center">Recommended</h3>
<h3 style="text-align:center">Recommended</h3>
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<td style="text-align:center" valign="middle">
<a href="https://sourl.cn/e7ee87">
<img src="./media/sponsor/xingqiu.png" style="margin: 0 auto;width:850px" /></a>
</td>
@ -163,7 +163,7 @@ In addition[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-alg
**Important knowledge points:**
1. <div align="center">
1. <div style="text-align:center">
<p>
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
<img src="https://img-blog.csdnimg.cn/img_convert/1c00413c65d1995993bf2b0daf7b4f03.png#pic_center" width="" />

View File

@ -1,6 +1,6 @@
> [JavaGuide 官方知识星球](https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc)来啦!!!如果你需要专属面试小册/一对一交流/简历修改/专属求职指南/学习打卡,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc) (一定要确定自己真的需要再加入,一定要看完详细介绍之后再加我)。
<div align="center">
<div style="text-align:center">
<p>
<a href="https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc">
<img src="./media/sponsor/xingqiu.png" style="margin: 0 auto; width: 850px;" />
@ -100,13 +100,12 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
### 新特性
1. **Java 8** [Java 8 新特性总结(翻译)](docs/java/new-features/java8-tutorial-translate.md)、[Java8常用新特性总结](docs/java/new-features/java8-common-new-features.md)
2. **Java9~Java15** : [一文带你看遍 JDK9~15 的重要新特性!](./docs/java/new-features/java新特性总结.md)
### 小技巧
1. [JAD 反编译](docs/java/tips/jad.md)
2. [手把手教你定位常见 Java 性能问题](./docs/java/tips/locate-performance-problems/手把手教你定位常见Java性能问题.md)
1. **Java 8** [Java 8 新特性总结(翻译)](docs/java/new-features/java8-tutorial-translate.md)、[Java8常用新特性总结](./docs/java/new-features/java8-common-new-features.md)
2. [Java 9 新特性概览](./docs/java/new-features/java9.md)
3. [Java 10 新特性概览](./docs/java/new-features/java10.md)
4. [Java 11 新特性概览](./docs/java/new-features/java11.md)
5. [Java 12~13 新特性概览](./docs/java/new-features/java12-13.md)
6. [Java 14~15 新特性概览](./docs/java/new-features/java14-15.md)
## 计算机基础
@ -114,7 +113,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
### 操作系统
1. [操作系统常见问题总结!](docs/cs-basics/operating-system/操作系统常见面试题&知识点总结.md)
1. [操作系统常见问题总结!](docs/cs-basics/operating-system/operating-system-basic-questions-01.md)
2. [后端程序员必备的 Linux 基础知识总结](docs/cs-basics/operating-system/linux-intro.md)
3. [Shell 编程入门](docs/cs-basics/operating-system/shell-intro.md)
@ -130,10 +129,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
**图解数据结构:**
1. [线性数据结构 :数组、链表、栈、队列](docs/cs-basics/data-structure/线性数据结构.md)
2. [](docs/cs-basics/data-structure/.md)
3. [](docs/cs-basics/data-structure/.md)
4. [](docs/cs-basics/data-structure/树.md) :重点关注[红黑树](docs/cs-basics/data-structure/红黑树.md)、B-B+B*树、LSM树
1. [线性数据结构 :数组、链表、栈、队列](docs/cs-basics/data-structure/linear-data-structure.md)
2. [](docs/cs-basics/data-structure/graph.md)
3. [](docs/cs-basics/data-structure/heap.md)
4. [](docs/cs-basics/data-structure/tree.md) :重点关注[红黑树](docs/cs-basics/data-structure/red-black-tree.md)、B-B+B*树、LSM树
其他常用数据结构
@ -148,9 +147,9 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
**常见算法问题总结**
- [几道常见的字符串算法题总结 ](docs/cs-basics/algorithms/几道常见的字符串算法题.md)
- [几道常见的链表算法题总结 ](docs/cs-basics/algorithms/几道常见的链表算法题.md)
- [剑指 offer 部分编程题](docs/cs-basics/algorithms/剑指offer部分编程题.md)
- [几道常见的字符串算法题总结 ](docs/cs-basics/algorithms/string-algorithm-problems.md)
- [几道常见的链表算法题总结 ](docs/cs-basics/algorithms/linkedlist-algorithm-problems.md)
- [剑指 offer 部分编程题](docs/cs-basics/algorithms/the-sword-refers-to-offer.md)
另外,[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-algorithms/) 这个网站总结了常见的算法 ,比较全面系统。
@ -160,8 +159,8 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
**总结:**
1. [数据库基础知识总结](docs/database/数据库基础知识.md)
2. **[MySQL知识点总结](docs/database/mysql/mysql知识点&面试题总结.md)** (必看 :+1:)
1. [数据库基础知识总结](docs/database/basis.md)
2. **[MySQL知识点总结](docs/database/mysql/mysql-questions-01.md)** (必看 :+1:)
4. [一千行 MySQL 学习笔记](docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md)
5. [MySQL 高性能优化规范建议](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md)
@ -172,7 +171,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
3. [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/mysql-logs.md)
4. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/innodb-implementation-of-mvcc.md)
5. [一条 SQL 语句在 MySQL 中如何被执行的?](docs/database/mysql/how-sql-executed-in-mysql.md)
6. [字符集详解为什么不建议在MySQL中使用 utf8 ](docs/database/字符集.md)
6. [字符集详解为什么不建议在MySQL中使用 utf8 ](docs/database/character-set.md)
7. [关于数据库中如何存储时间的一点思考](docs/database/mysql/some-thoughts-on-database-storage-time.md)
### Redis
@ -224,7 +223,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
#### Spring Cloud
[ 大白话入门 Spring Cloud](docs/system-design/framework/springcloud/springcloud-intro.md)
[大白话入门 Spring Cloud](docs/system-design/framework/springcloud/springcloud-intro.md)
### 安全
@ -246,11 +245,11 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
系统需要对用户输入的文本进行敏感词过滤如色情、政治、暴力相关的词汇。
相关阅读:[Java定时任务大揭秘》](./docs/system-design/security/sentive-words-filter.md)
相关阅读:[敏感词过滤》](./docs/system-design/security/sentive-words-filter.md)
### 定时任务
最近有朋友问到定时任务相关的问题。于是,我简单写了一篇文章总结一下定时任务的一些概念以及一些常见的定时任务技术选型:[《Java定时任务大揭秘》](./docs/system-design/定时任务.md)
最近有朋友问到定时任务相关的问题。于是,我简单写了一篇文章总结一下定时任务的一些概念以及一些常见的定时任务技术选型:[《Java定时任务大揭秘》](./docs/system-design/schedule-task.md)
## 分布式
@ -317,7 +316,7 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读:
1. **RabbitMQ** : [RabbitMQ 入门](docs/high-performance/message-queue/rabbitmq-intro.md)
2. **RocketMQ** : [RocketMQ 入门](docs/high-performance/message-queue/rocketmq-intro)、[RocketMQ 的几个简单问题与答案](docs/high-performance/message-queue/rocketmq-questions.md)
3. **Kafka** [Kafka 常见问题总结](docs/high-performance/message-queue/kafka知识点&面试题总结.md)
3. **Kafka** [Kafka 常见问题总结](docs/high-performance/message-queue/kafka-questions-01.md)
### 读写分离&分库分表
@ -329,7 +328,7 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读:
常见的分库分表工具有:`sharding-jdbc`(当当)、`TSharding`(蘑菇街)、`MyCAT`(基于 Cobar`Cobar`(阿里巴巴)...。 推荐使用 `sharding-jdbc`。 因为,`sharding-jdbc` 是一款轻量级 `Java` 框架,以 `jar` 包形式提供服务,不要我们做额外的运维工作,并且兼容性也很好。
相关阅读: [读写分离&分库分表常见问题总结](docs/high-performance/读写分离&分库分表.md)
相关阅读: [读写分离&分库分表常见问题总结](docs/high-performance/read-and-write-separation-and-library-subtable.md)
### 负载均衡
@ -345,7 +344,7 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读:
高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。
相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/high-availability/高可用系统设计.md)》** 。
相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/high-availability/high-availability-system-design.md)》** 。
### 限流

View File

@ -1,429 +0,0 @@
const { config } = require("vuepress-theme-hope");
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = config({
port: "8080",
title: "JavaGuide",
description: "Java学习&&面试指南",
//指定 vuepress build 的输出目录
dest: "./dist",
// 是否开启默认预加载js
shouldPrefetch: (file, type) => false,
// webpack 配置 https://vuepress.vuejs.org/zh/config/#chainwebpack
// chainWebpack: config => {
// if (process.env.NODE_ENV === 'production') {
// const dateTime = new Date().getTime();
// // 清除js版本号
// config.output.filename('assets/js/jg-[name].js?v=' + dateTime).end();
// config.output.chunkFilename('assets/js/jg-[name].js?v=' + dateTime).end();
// // 清除css版本号
// config.plugin('mini-css-extract-plugin').use(require('mini-css-extract-plugin'), [{
// filename: 'assets/css/[name].css?v=' + dateTime,
// chunkFilename: 'assets/css/[name].css?v=' + dateTime
// }]).end();
// }
// },
configureWebpack: {
//vuepress 编译压缩
plugins: [new CompressionPlugin({
filename: "[path].gz", //编译后的文件名
algorithm: "gzip",
test: /\.js$|\.css$|\.html$/,//需要编译的文件
threshold: 10240,//需要编译的文件大小
minRatio: 0.8,//压缩比
deleteOriginalAssets: false,//编译时是否删除源文件
})],
},
head: [
// 百度站点验证
["meta", { name: "baidu-site-verification", content: "code-IZvTs9l2OK" }],
[
"script",
{ src: "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js" },
],
[
"script",
{
src: "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js",
},
],
["script", { src: "https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js" }],
[
"script",
{ src: "https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js" },
],
// 添加百度统计
[
"script", {},
`var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?5dd2e8c97962d57b7b8fea1737c01743";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`
]
],
locales: {
"/": {
lang: "zh-CN"
}
},
themeConfig: {
logo: "/logo.png", hostname: "https://javaguide.cn/", author: "Guide哥", repo: "https://github.com/Snailclimb/JavaGuide",
editLinks: true, docsDir: 'docs', seo: true,
nav: [
{ text: "Java面试指南", icon: "java", link: "/home", },
{ text: "Java面试指北", icon: "recommend", link: "https://www.yuque.com/docs/share/f37fc804-bfe6-4b0d-b373-9c462188fec7" },
{ text: "官方知识星球", icon: "recommend", link: "https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc" },
{
text: "开发工具", icon: "Tools", link: "/tools/",
items: [
{ text: "Java", icon: "java", link: "/tools/java/jadx/" },
{ text: "Database", icon: "database", link: "/tools/database/chiner/" },
{ text: "Git", icon: "git", link: "/tools/git/git-intro/" },
{ text: "Docker", icon: "docker1", link: "/tools/docker/docker-intro/" },
{ text: "IntelliJ IDEA", icon: "intellijidea", link: "/idea-tutorial/" },
]
},
{ text: "关于作者", icon: "zuozhe", link: "/about-the-author/" },
],
sidebar: {
// 应该把更精确的路径放置在前边
"/javaguide/": [
"intro", "contribution-guideline", "faq", "todo"
],
"/about-the-author/": [
{
title: "个人经历", icon: "zuozhe", collapsable: false,
children: ["internet-addiction-teenager", "javaguide-100k-star", "feelings-after-one-month-of-induction-training", "feelings-of-half-a-year-from-graduation-to-entry",]
},
{
title: "杂谈", icon: "chat", collapsable: false,
children: ["my-article-was-stolen-and-made-into-video-and-it-became-popular", "dog-that-copies-other-people-essay",]
},
],
'/tools/': [
{
title: "Java", icon: "java", prefix: "java/", collapsable: false,
children: ["jadx"]
},
{
title: "Database", icon: "database", prefix: "database/", collapsable: false,
children: ["chiner", "dbeaver", "screw", "datagrip"]
},
{
title: "Git", icon: "git", prefix: "git/", collapsable: false,
children: ["git-intro", "github-tips"]
},
{
title: "Docker", icon: "docker1", prefix: "docker/", collapsable: false,
children: ["docker-intro", "docker-in-action"]
},
],
'/high-quality-technical-articles/': [
{
title: "练级攻略", icon: "lujing", prefix: "advanced-programmer/", collapsable: false,
children: ["seven-tips-for-becoming-an-advanced-programmer"]
},
{
title: "个人经历", icon: "zuozhe", prefix: "personal-experience/", collapsable: false,
children: ["two-years-of-back-end-develop--experience-in-didi&toutiao", "8-years-programmer-work-summary"]
},
{
title: "面试", icon: "mianshixinxi-02", prefix: "interview/", collapsable: false,
children: ["the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer", "technical-preliminary-preparation", "screen-candidates-for-packaging"],
},
{
title: "工作", icon: "work0", prefix: "work/", collapsable: false,
children: ["get-into-work-mode-quickly-when-you-join-a-company"]
}
],
'/idea-tutorial/':
[
{
title: "IDEA小技巧", icon: "tips", prefix: "idea-tips/", collapsable: false,
children: [
"idea-refractor-intro",
"idea-plug-in-development-intro",
"idea-source-code-reading-skills",
]
},
{
title: "IDEA插件推荐", icon: "chajian1", collapsable: false, prefix: "idea-plugins/",
children: [
"shortcut-key", "idea-themes", "improve-code", "interface-beautification",
"camel-case", "code-glance", "code-statistic",
"git-commit-template", "gson-format", "idea-features-trainer", "jclasslib",
"maven-helper", "rest-devlop", "save-actions", "sequence-diagram", "translation",
"others"
]
},
],
// 必须放在最后面
'/': [{
title: "Java", icon: "java", prefix: "java/",
children: [
{
title: "基础", prefix: "basis/",
children: [
"java-basic-questions-01", "java-basic-questions-02", "java-basic-questions-03",
{
title: "重要知识点",
children: [
"why-there-only-value-passing-in-java", "reflection", "proxy", "io",
"bigdecimal", "generics"
],
},],
},
{
title: "容器", prefix: "collection/",
children: [
"java-collection-questions-01", "java-collection-questions-02", "java集合使用注意事项",
{
title: "源码分析",
children: ["arraylist-source-code", "hashmap-source-code", "concurrent-hash-map-source-code"],
},],
},
{
title: "并发编程", prefix: "concurrent/",
children: [
"java-concurrent-questions-01", "java-concurrent-questions-02",
{
title: "重要知识点",
children: ["java-thread-pool-summary", "java-thread-pool-best-practices", "java-concurrent-collections", "aqs", "reentrantlock",
"atomic-classes", "threadlocal", "completablefuture-intro"],
},
],
},
{
title: "JVM", prefix: "jvm/",
children: ["memory-area", "jvm-garbage-collection", "class-file-structure", "class-loading-process", "classloader", "jvm-parameters-intro", "jvm-intro", "jdk-monitoring-and-troubleshooting-tools"],
},
{
title: "新特性", prefix: "new-features/",
children: ["java8-common-new-features", "java8-tutorial-translate", "java9", "java10", "java11", "java12-13", "java14-15"],
},
{
title: "小技巧", prefix: "tips/",
children: ["locate-performance-problems/手把手教你定位常见Java性能问题", "jad"],
},
],
},
{
title: "计算机基础", icon: "computer", prefix: "cs-basics/",
children: [
{
title: "网络", prefix: "network/", icon: "network",
children: [
"osi&tcp-ip-model", "http&https", "http1.0&http1.1",
"other-network-questions"
],
},
{
title: "操作系统", prefix: "operating-system/", icon: "caozuoxitong",
children: [
"操作系统常见面试题&知识点总结", "linux-intro", "shell-intro"
],
},
{
title: "数据结构", prefix: "data-structure/", icon: "people-network-full",
children: [
"线性数据结构", "图", "堆", "树", "红黑树", "bloom-filter"
],
},
{
title: "算法", prefix: "algorithms/", icon: "suanfaku",
children: [
"几道常见的字符串算法题", "几道常见的链表算法题", "剑指offer部分编程题"
],
},
],
},
{
title: "数据库", icon: "database", prefix: "database/",
children: [
"数据库基础知识",
"字符集",
{
title: "MySQL", prefix: "mysql/", icon: "mysql",
children: [
"mysql知识点&面试题总结",
"a-thousand-lines-of-mysql-study-notes",
"mysql-high-performance-optimization-specification-recommendations",
"mysql-index", "mysql-logs", "transaction-isolation-level",
"innodb-implementation-of-mvcc", "how-sql-executed-in-mysql",
"some-thoughts-on-database-storage-time"
],
},
{
title: "Redis", prefix: "redis/", icon: "redis",
children: ["redis-questions-01", "3-commonly-used-cache-read-and-write-strategies"],
},
],
},
{
title: "系统设计", icon: "xitongsheji", prefix: "system-design/",
children: [
"system-design-questions",
{
title: "基础", prefix: "basis/", icon: "jibendebasic",
children: [
"RESTfulAPI",
"naming",
],
},
{
title: "常用框架", prefix: "framework/", icon: "framework",
children: [{
title: "Spring", prefix: "spring/",
children: [
"spring-knowledge-and-questions-summary", "spring-common-annotations", "spring-transaction", "spring-design-patterns-summary", "spring-boot-auto-assembly-principles"
]
},
"mybatis/mybatis-interview", "netty",
{
title: "SpringCloud", prefix: "springcloud/",
children: ["springcloud-intro"]
},
],
},
{
title: "安全", prefix: "security/", icon: "security-fill",
children: ["basis-of-authority-certification", "advantages&disadvantages-of-jwt", "sso-intro", "sentive-words-filter", "data-desensitization"]
},
"定时任务"
],
},
{
title: "分布式", icon: "distributed-network", prefix: "distributed-system/",
children: [
{
title: "理论&算法&协议", prefix: "theorem&algorithm&protocol/",
children: ["cap&base-theorem", "paxos-algorithm", "raft-algorithm"],
},
"api-gateway", "distributed-id",
{
title: "RPC", prefix: "rpc/",
children: ["dubbo", "why-use-rpc"]
},
"distributed-transaction",
{
title: "分布式协调", prefix: "distributed-process-coordination/",
children: ["zookeeper/zookeeper-intro", "zookeeper/zookeeper-plus", "zookeeper/zookeeper-in-action"]
},
],
}, {
title: "高性能", icon: "gaojixiaozuzhibeifen", prefix: "high-performance/",
children: [
"读写分离&分库分表", "负载均衡",
{
title: "消息队列", prefix: "message-queue/", icon: "MQ",
children: ["message-queue", "kafka知识点&面试题总结", "rocketmq-intro", "rocketmq-questions", "rabbitmq-intro"],
},
],
}, {
title: "高可用", icon: "CalendarAvailability-1", prefix: "high-availability/",
children: [
"高可用系统设计", "limit-request", "降级&熔断", "超时和重试机制", "集群", "灾备设计和异地多活", "性能测试"
],
}],
},
blog: {
intro: "/about-the-author/",
sidebarDisplay: "mobile",
links: {
Zhihu: "https://www.zhihu.com/people/javaguide",
Github: "https://github.com/Snailclimb",
Gitee: "https://gitee.com/SnailClimb",
},
},
footer: {
display: true,
content: '<a href="https://beian.miit.gov.cn/" target="_blank">鄂ICP备2020015769号-1</a>',
},
copyright: {
status: "global",
},
git: {
timezone: "Asia/Shanghai",
},
mdEnhance: {
enableAll: false,
presentation: {
plugins: [
"highlight", "math", "search", "notes", "zoom", "anything", "audio", "chalkboard",
],
},
},
pwa: {
favicon: "/favicon.ico",
cachePic: false,
cacheHTML: false,
apple: {
icon: "/assets/icon/apple-icon-152.png",
statusBarColor: "black",
},
msTile: {
image: "/assets/icon/ms-icon-144.png",
color: "#ffffff",
},
manifest: {
icons: [
{
src: "/assets/icon/chrome-mask-512.png",
sizes: "512x512",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/chrome-mask-192.png",
sizes: "192x192",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/chrome-512.png",
sizes: "512x512",
type: "image/png",
},
{
src: "/assets/icon/chrome-192.png",
sizes: "192x192",
type: "image/png",
},
],
shortcuts: [
{
name: "Guide",
short_name: "Guide",
url: "/guide/",
icons: [
{
src: "/assets/icon/guide-maskable.png",
sizes: "192x192",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/guide-monochrome.png",
sizes: "192x192",
purpose: "monochrome",
type: "image/png",
},
],
},
],
},
},
},
});

55
docs/.vuepress/config.ts Normal file
View File

@ -0,0 +1,55 @@
const { defineHopeConfig } = require("vuepress-theme-hope");
import themeConfig from "./themeConfig";
module.exports = defineHopeConfig({
port: "8080",
title: "JavaGuide",
description: "Java学习&&面试指南",
//指定 vuepress build 的输出目录
dest: "./dist",
// 是否开启默认预加载 js
shouldPrefetch: (file, type) => false,
head: [
// 百度站点验证
["meta", { name: "baidu-site-verification", content: "code-IZvTs9l2OK" }],
[
"script",
{ src: "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js" },
],
[
"script",
{
src: "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js",
},
],
["script", { src: "https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js" }],
[
"script",
{ src: "https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js" },
],
// 添加百度统计
[
"script", {},
`var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?5dd2e8c97962d57b7b8fea1737c01743";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`
],
[
"link",
{
rel: "stylesheet",
href: "//at.alicdn.com/t/font_2922463_zpzjti5jsah.css",
},
],
],
locales: {
"/": {
lang: "zh-CN"
}
},
themeConfig,
});

28
docs/.vuepress/navbar.ts Normal file
View File

@ -0,0 +1,28 @@
import { defineNavbarConfig } from "vuepress-theme-hope";
export const navbarConfig = defineNavbarConfig([
{ text: "Java面试指南", icon: "java", link: "/home" },
{
text: "Java面试指北",
icon: "recommend",
link: "https://www.yuque.com/docs/share/f37fc804-bfe6-4b0d-b373-9c462188fec7",
},
{
text: "官方知识星球",
icon: "recommend",
link: "https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc",
},
{
text: "开发工具",
icon: "Tools",
link: "/tools/",
children: [
{ text: "Java", icon: "java", link: "/tools/java/jadx/" },
{ text: "Database", icon: "database", link: "/tools/database/chiner/" },
{ text: "Git", icon: "git", link: "/tools/git/git-intro/" },
{ text: "Docker", icon: "docker1", link: "/tools/docker/docker-intro/" },
{ text: "IntelliJ IDEA", icon: "intellijidea", link: "/idea-tutorial/" },
],
},
{ text: "关于作者", icon: "zuozhe", link: "/about-the-author/" },
]);

472
docs/.vuepress/sidebar.ts Normal file
View File

@ -0,0 +1,472 @@
import { defineSidebarConfig } from "vuepress-theme-hope";
export const sidebarConfig = defineSidebarConfig({
// 应该把更精确的路径放置在前边
"/javaguide/": ["intro", "contribution-guideline", "faq", "todo"],
"/about-the-author/": [
{
text: "个人经历",
icon: "zuozhe",
collapsable: false,
children: [
"internet-addiction-teenager",
"javaguide-100k-star",
"feelings-after-one-month-of-induction-training",
"feelings-of-half-a-year-from-graduation-to-entry",
],
},
{
text: "杂谈",
icon: "chat",
collapsable: false,
children: [
"my-article-was-stolen-and-made-into-video-and-it-became-popular",
"dog-that-copies-other-people-essay",
],
},
],
"/tools/": [
{
text: "Java",
icon: "java",
prefix: "java/",
collapsable: false,
children: ["jadx"],
},
{
text: "Database",
icon: "database",
prefix: "database/",
collapsable: false,
children: ["chiner", "dbeaver", "screw", "datagrip"],
},
{
text: "Git",
icon: "git",
prefix: "git/",
collapsable: false,
children: ["git-intro", "github-tips"],
},
{
text: "Docker",
icon: "docker1",
prefix: "docker/",
collapsable: false,
children: ["docker-intro", "docker-in-action"],
},
],
"/high-quality-technical-articles/": [
{
text: "练级攻略",
icon: "et-performance",
prefix: "advanced-programmer/",
collapsable: false,
children: ["seven-tips-for-becoming-an-advanced-programmer"],
},
{
text: "个人经历",
icon: "zuozhe",
prefix: "personal-experience/",
collapsable: false,
children: [
"two-years-of-back-end-develop--experience-in-didi&toutiao",
"8-years-programmer-work-summary",
],
},
{
text: "面试",
icon: "mianshixinxi-02",
prefix: "interview/",
collapsable: false,
children: [
"the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer",
"technical-preliminary-preparation",
"screen-candidates-for-packaging",
],
},
{
text: "工作",
icon: "work0",
prefix: "work/",
collapsable: false,
children: ["get-into-work-mode-quickly-when-you-join-a-company"],
},
],
"/idea-tutorial/": [
{
text: "IDEA小技巧",
icon: "tips",
prefix: "idea-tips/",
collapsable: false,
children: [
"idea-refractor-intro",
"idea-plug-in-development-intro",
"idea-source-code-reading-skills",
],
},
{
text: "IDEA插件推荐",
icon: "chajian1",
collapsable: false,
prefix: "idea-plugins/",
children: [
"shortcut-key",
"idea-themes",
"improve-code",
"interface-beautification",
"camel-case",
"code-glance",
"code-statistic",
"git-commit-template",
"gson-format",
"idea-features-trainer",
"jclasslib",
"maven-helper",
"rest-devlop",
"save-actions",
"sequence-diagram",
"translation",
"others",
],
},
],
// 必须放在最后面
"/": [
{
text: "Java",
icon: "java",
prefix: "java/",
collapsable: true,
children: [
{
text: "基础",
prefix: "basis/",
icon: "basic",
collapsable: true,
children: [
"java-basic-questions-01",
"java-basic-questions-02",
"java-basic-questions-03",
{
text: "重要知识点",
icon: "important",
collapsable: true,
children: [
"why-there-only-value-passing-in-java",
"reflection",
"proxy",
"io",
"bigdecimal",
"generics",
],
},
],
},
{
text: "容器",
prefix: "collection/",
icon: "container",
collapsable: true,
children: [
"java-collection-questions-01",
"java-collection-questions-02",
"java-collection-precautions-for-use",
{
text: "源码分析",
collapsable: true,
children: [
"arraylist-source-code",
"hashmap-source-code",
"concurrent-hash-map-source-code",
],
},
],
},
{
text: "并发编程",
prefix: "concurrent/",
icon: "et-performance",
collapsable: true,
children: [
"java-concurrent-questions-01",
"java-concurrent-questions-02",
{
text: "重要知识点",
icon: "important",
collapsable: true,
children: [
"java-thread-pool-summary",
"java-thread-pool-best-practices",
"java-concurrent-collections",
"aqs",
"reentrantlock",
"atomic-classes",
"threadlocal",
"completablefuture-intro",
],
},
],
},
{
text: "JVM",
prefix: "jvm/",
icon: "virtual_machine",
collapsable: true,
children: [
"memory-area",
"jvm-garbage-collection",
"class-file-structure",
"class-loading-process",
"classloader",
"jvm-parameters-intro",
"jvm-intro",
"jdk-monitoring-and-troubleshooting-tools",
],
},
{
text: "新特性",
prefix: "new-features/",
icon: "features",
collapsable: true,
children: [
"java8-common-new-features",
"java8-tutorial-translate",
"java9",
"java10",
"java11",
"java12-13",
"java14-15",
],
},
],
},
{
text: "计算机基础",
icon: "computer",
prefix: "cs-basics/",
collapsable: true,
children: [
{
text: "网络",
prefix: "network/",
icon: "network",
collapsable: true,
children: [
"osi&tcp-ip-model",
"http&https",
"http1.0&http1.1",
"other-network-questions",
],
},
{
text: "操作系统",
prefix: "operating-system/",
icon: "caozuoxitong",
collapsable: true,
children: [
"operating-system-basic-questions-01",
"linux-intro",
"shell-intro",
],
},
{
text: "数据结构",
prefix: "data-structure/",
icon: "people-network-full",
collapsable: true,
children: [
"linear-data-structure",
"graph",
"heap",
"tree",
"red-black-tree",
"bloom-filter",
],
},
{
text: "算法",
prefix: "algorithms/",
icon: "suanfaku",
collapsable: true,
children: [
"string-algorithm-problems",
"linkedlist-algorithm-problems",
"the-sword-refers-to-offer",
],
},
],
},
{
text: "数据库",
icon: "database",
prefix: "database/",
collapsable: true,
children: [
"basis",
"character-set",
{
text: "MySQL",
prefix: "mysql/",
icon: "mysql",
collapsable: true,
children: [
"mysql-questions-01",
"a-thousand-lines-of-mysql-study-notes",
"mysql-high-performance-optimization-specification-recommendations",
{
text: "重要知识点",
icon: "important",
collapsable: true,
children: [
"mysql-index",
"mysql-logs",
"transaction-isolation-level",
"innodb-implementation-of-mvcc",
"how-sql-executed-in-mysql",
"some-thoughts-on-database-storage-time",
],
},
],
},
{
text: "Redis",
prefix: "redis/",
icon: "redis",
collapsable: true,
children: [
"redis-questions-01",
"3-commonly-used-cache-read-and-write-strategies",
],
},
],
},
{
text: "系统设计",
icon: "xitongsheji",
prefix: "system-design/",
collapsable: true,
children: [
"system-design-questions",
{
text: "基础",
prefix: "basis/",
icon: "basic",
collapsable: true,
children: ["RESTfulAPI", "naming"],
},
{
text: "常用框架",
prefix: "framework/",
icon: "framework",
collapsable: true,
children: [
{
text: "Spring",
prefix: "spring/",
collapsable: true,
children: [
"spring-knowledge-and-questions-summary",
"spring-common-annotations",
"spring-transaction",
"spring-design-patterns-summary",
"spring-boot-auto-assembly-principles",
],
},
"mybatis/mybatis-interview",
"netty",
{
text: "SpringCloud",
prefix: "springcloud/",
children: ["springcloud-intro"],
},
],
},
{
text: "安全",
prefix: "security/",
icon: "security-fill",
collapsable: true,
children: [
"basis-of-authority-certification",
"advantages&disadvantages-of-jwt",
"sso-intro",
"sentive-words-filter",
"data-desensitization",
],
},
"schedule-task",
],
},
{
text: "分布式",
icon: "distributed-network",
prefix: "distributed-system/",
collapsable: true,
children: [
{
text: "理论&算法&协议",
prefix: "theorem&algorithm&protocol/",
collapsable: true,
children: ["cap&base-theorem", "paxos-algorithm", "raft-algorithm"],
},
"api-gateway",
"distributed-id",
{
text: "RPC",
prefix: "rpc/",
collapsable: true,
children: ["dubbo", "why-use-rpc"],
},
"distributed-transaction",
{
text: "分布式协调",
prefix: "distributed-process-coordination/",
collapsable: true,
children: [
"zookeeper/zookeeper-intro",
"zookeeper/zookeeper-plus",
"zookeeper/zookeeper-in-action",
],
},
],
},
{
text: "高性能",
icon: "et-performance",
prefix: "high-performance/",
collapsable: true,
children: [
"read-and-write-separation-and-library-subtable",
"load-balancing",
{
text: "消息队列",
prefix: "message-queue/",
icon: "MQ",
collapsable: true,
children: [
"message-queue",
"kafka-questions-01",
"rocketmq-intro",
"rocketmq-questions",
"rabbitmq-intro",
],
},
],
},
{
text: "高可用",
icon: "CalendarAvailability-1",
prefix: "high-availability/",
collapsable: true,
children: [
"high-availability-system-design",
"limit-request",
"fallback&circuit-breaker",
"timeout-and-retry",
"cluster",
"disaster-recovery&remote-live",
"performance-test",
],
},
],
});

View File

View File

@ -1,2 +0,0 @@
// import icon
@import '//at.alicdn.com/t/font_2922463_j457c9usui.css'

View File

@ -0,0 +1,4 @@
// colors
$themeColor: #2980B9;
$sidebarWidth: 20rem;
$sidebarMobileWidth: 16rem;

View File

@ -0,0 +1,98 @@
import { defineThemeConfig } from "vuepress-theme-hope";
import { navbarConfig } from "./navbar";
import { sidebarConfig } from "./sidebar";
export default defineThemeConfig({
logo: "/logo.png",
hostname: "https://javaguide.cn/",
author: "Guide哥",
repo: "https://github.com/Snailclimb/JavaGuide",
docsDir: "docs",
iconPrefix: "iconfont icon-",
pure: true,
navbar: navbarConfig,
sidebar: sidebarConfig,
blog: {
intro: "/about-the-author/",
sidebarDisplay: "mobile",
medias: {
Zhihu: "https://www.zhihu.com/people/javaguide",
Github: "https://github.com/Snailclimb",
Gitee: "https://gitee.com/SnailClimb",
},
},
footer:
'<a href="https://beian.miit.gov.cn/" target="_blank">鄂ICP备2020015769号-1</a>',
displayFooter: true,
plugins: {
blog: {
autoExcerpt: true,
},
mdEnhance: {
enableAll: false,
presentation: {
plugins: ["highlight", "math", "search", "notes", "zoom"],
},
},
pwa: {
favicon: "/favicon.ico",
cachePic: false,
cacheHTML: false,
apple: {
icon: "/assets/icon/apple-icon-152.png",
statusBarColor: "black",
},
msTile: {
image: "/assets/icon/ms-icon-144.png",
color: "#ffffff",
},
manifest: {
icons: [
{
src: "/assets/icon/chrome-mask-512.png",
sizes: "512x512",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/chrome-mask-192.png",
sizes: "192x192",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/chrome-512.png",
sizes: "512x512",
type: "image/png",
},
{
src: "/assets/icon/chrome-192.png",
sizes: "192x192",
type: "image/png",
},
],
shortcuts: [
{
name: "Guide",
short_name: "Guide",
url: "/guide/",
icons: [
{
src: "/assets/icon/guide-maskable.png",
sizes: "192x192",
purpose: "maskable",
type: "image/png",
},
{
src: "/assets/icon/guide-monochrome.png",
sizes: "192x192",
purpose: "monochrome",
type: "image/png",
},
],
},
],
},
},
},
});

View File

@ -23,7 +23,7 @@ tag:
下图所展示的就是图这种数据结构,并且还是一张有向图。
![](pictures/图/图.png)
![](https://img-blog.csdnimg.cn/7f232c9660c54ee1ac182b7c0bf267a3.png)
图在我们日常生活中的例子很多!比如我们在社交软件上好友关系就可以用图来表示。
@ -67,11 +67,11 @@ tag:
在无向图中我们只关心关系的有无所以当顶点i和顶点j有关系时`A[i][j]`=1当顶点i和顶点j没有关系时`A[i][j]`=0。如下图所示
![无向图的邻接矩阵存储](pictures/图/无向图的邻接矩阵存储.png)
![无向图的邻接矩阵存储](./pictures/图/无向图的邻接矩阵存储.png)
值得注意的是:**无向图的邻接矩阵是一个对称矩阵因为在无向图中顶点i和顶点j有关系则顶点j和顶点i必有关系。**
![有向图的邻接矩阵存储](pictures/图/有向图的邻接矩阵存储.png)
![有向图的邻接矩阵存储](./pictures/图/有向图的邻接矩阵存储.png)
邻接矩阵存储的方式优点是简单直接(直接使用一个二维数组即可),并且,在获取两个定点之间的关系的时候也非常高效(直接获取指定位置的数组元素的值即可)。但是,这种存储方式的缺点也比较明显,那就是比较浪费空间,
@ -83,11 +83,11 @@ tag:
![无向图的邻接表存储](pictures/图/无向图的邻接表存储.png)
![无向图的邻接表存储](./pictures/图/无向图的邻接表存储.png)
![有向图的邻接表存储](pictures/图/有向图的邻接表存储.png)
![有向图的邻接表存储](./pictures/图/有向图的邻接表存储.png)
大家可以数一数邻接表中所存储的元素的个数以及图中边的条数,你会发现:
@ -98,64 +98,64 @@ tag:
### 广度优先搜索
广度优先搜索就像水面上的波纹一样一层一层向外扩展,如下图所示:
![广度优先搜索图示](pictures/图/广度优先搜索图示.png)
![广度优先搜索图示](./pictures/图/广度优先搜索图示.png)
**广度优先搜索的具体实现方式用到了之前所学过的线性数据结构——队列** 。具体过程如下图所示:
**第1步**
![广度优先搜索1](pictures/图/广度优先搜索1.png)
![广度优先搜索1](./pictures/图/广度优先搜索1.png)
**第2步**
![广度优先搜索2](pictures/图/广度优先搜索2.png)
![广度优先搜索2](./pictures/图/广度优先搜索2.png)
**第3步**
![广度优先搜索3](pictures/图/广度优先搜索3.png)
![广度优先搜索3](./pictures/图/广度优先搜索3.png)
**第4步**
![广度优先搜索4](pictures/图/广度优先搜索4.png)
![广度优先搜索4](./pictures/图/广度优先搜索4.png)
**第5步**
![广度优先搜索5](pictures/图/广度优先搜索5.png)
![广度优先搜索5](./pictures/图/广度优先搜索5.png)
**第6步**
![广度优先搜索6](pictures/图/广度优先搜索6.png)
![广度优先搜索6](./pictures/图/广度优先搜索6.png)
### 深度优先搜索
深度优先搜索就是“一条路走到黑”,从源顶点开始,一直走到没有后继节点,才回溯到上一顶点,然后继续“一条路走到黑”,如下图所示:
![深度优先搜索图示](pictures/图/深度优先搜索图示.png)
![深度优先搜索图示](./pictures/图/深度优先搜索图示.png)
**和广度优先搜索类似,深度优先搜索的具体实现用到了另一种线性数据结构——栈** 。具体过程如下图所示:
**第1步**
![深度优先搜索1](pictures/图/深度优先搜索1.png)
![深度优先搜索1](./pictures/图/深度优先搜索1.png)
**第2步**
![深度优先搜索1](pictures/图/深度优先搜索2.png)
![深度优先搜索1](./pictures/图/深度优先搜索2.png)
**第3步**
![深度优先搜索1](pictures/图/深度优先搜索3.png)
![深度优先搜索1](./pictures/图/深度优先搜索3.png)
**第4步**
![深度优先搜索1](pictures/图/深度优先搜索4.png)
![深度优先搜索1](./pictures/图/深度优先搜索4.png)
**第5步**
![深度优先搜索1](pictures/图/深度优先搜索5.png)
![深度优先搜索1](./pictures/图/深度优先搜索5.png)
**第6步**
![深度优先搜索1](pictures/图/深度优先搜索6.png)
![深度优先搜索1](./pictures/图/深度优先搜索6.png)

View File

@ -21,7 +21,7 @@ tag:
大家可以尝试判断下面给出的图是否是堆?
![](pictures/堆/堆1.png)
![](./pictures/堆/堆1.png)
第1个和第2个是堆。第1个是最大堆每个节点都比子树中所有节点大。第2个是最小堆每个节点都比子树中所有节点小。
@ -42,7 +42,7 @@ tag:
如下图所示图1是最大堆图2是最小堆
![](pictures/堆/堆2.png)
![](./pictures/堆/堆2.png)
## 堆的存储
@ -50,7 +50,7 @@ tag:
为了方便存储和索引,(二叉)堆可以用完全二叉树的形式进行存储。存储的方式如下图所示:
![堆的存储](pictures/堆/堆的存储.png)
![堆的存储](./pictures/堆/堆的存储.png)
## 堆的操作
堆的更新操作主要包括两种 : **插入元素****删除堆顶元素**。操作过程需要着重掌握和理解。
@ -60,15 +60,15 @@ tag:
**1.将要插入的元素放到最后**
![堆-插入元素-1](pictures/堆/堆-插入元素1.png)
![堆-插入元素-1](./pictures/堆/堆-插入元素1.png)
> 有能力的人会逐渐升职加薪,是金子总会发光的!!!
**2.从底向上,如果父结点比该元素大,则该节点和父结点交换,直到无法交换**
![堆-插入元素2](pictures/堆/堆-插入元素2.png)
![堆-插入元素2](./pictures/堆/堆-插入元素2.png)
![堆-插入元素3](pictures/堆/堆-插入元素3.png)
![堆-插入元素3](./pictures/堆/堆-插入元素3.png)
### 删除堆顶元素
@ -87,34 +87,34 @@ tag:
![删除堆顶元素1](pictures/堆/删除堆顶元素1.png)
![删除堆顶元素1](./pictures/堆/删除堆顶元素1.png)
> 那么他的位置由谁来接替呢,当然是他的直接下属了,谁能力强就让谁上呗
比较根结点的左子节点和右子节点也就是下标为2,3的数组元素将较大的元素填充到根结点(下标为1)的位置。
![删除堆顶元素2](pictures/堆/删除堆顶元素2.png)
![删除堆顶元素2](./pictures/堆/删除堆顶元素2.png)
> 这个时候又空出一个位置了,老规矩,谁有能力谁上
一直循环比较空出位置的左右子节点,并将较大者移至空位,直到堆的最底部
![删除堆顶元素3](pictures/堆/删除堆顶元素3.png)
![删除堆顶元素3](./pictures/堆/删除堆顶元素3.png)
这个时候已经完成了自底向上的堆化,没有元素可以填补空缺了,但是,我们可以看到数组中出现了“气泡”,这会导致存储空间的浪费。接下来我们试试自顶向下堆化。
#### 自顶向下堆化
自顶向下的堆化用一个词形容就是“石沉大海”,那么第一件事情,就是把石头抬起来,从海面扔下去。这个石头就是堆的最后一个元素,我们将最后一个元素移动到堆顶。
![删除堆顶元素4](pictures/堆/删除堆顶元素4.png)
![删除堆顶元素4](./pictures/堆/删除堆顶元素4.png)
然后开始将这个石头沉入海底,不停与左右子节点的值进行比较,和较大的子节点交换位置,直到无法交换位置。
![删除堆顶元素5](pictures/堆/删除堆顶元素5.png)
![删除堆顶元素5](./pictures/堆/删除堆顶元素5.png)
![删除堆顶元素6](pictures/堆/删除堆顶元素6.png)
![删除堆顶元素6](./pictures/堆/删除堆顶元素6.png)
@ -139,20 +139,20 @@ tag:
具体过程如下图:
![建堆1](pictures/堆/建堆1.png)
![建堆1](./pictures/堆/建堆1.png)
将初始的无序数组抽象为一棵树图中的节点个数为6所以4,5,6节点为叶节点1,2,3节点为非叶节点所以要对1-3号节点进行自顶向下沉底堆化注意顺序是从后往前堆化从3号节点开始一直到1号节点。
3号节点堆化结果
![建堆1](pictures/堆/建堆2.png)
![建堆1](./pictures/堆/建堆2.png)
2号节点堆化结果
![建堆1](pictures/堆/建堆3.png)
![建堆1](./pictures/堆/建堆3.png)
1号节点堆化结果
![建堆1](pictures/堆/建堆4.png)
![建堆1](./pictures/堆/建堆4.png)
至此,数组所对应的树已经成为了一个最大堆,建堆完成!
@ -173,26 +173,26 @@ tag:
取出第一个元素并堆化:
![堆排序1](pictures/堆/堆排序1.png)
![堆排序1](./pictures/堆/堆排序1.png)
取出第二个元素并堆化:
![堆排序2](pictures/堆/堆排序2.png)
![堆排序2](./pictures/堆/堆排序2.png)
取出第三个元素并堆化:
![堆排序3](pictures/堆/堆排序3.png)
![堆排序3](./pictures/堆/堆排序3.png)
取出第四个元素并堆化:
![堆排序4](pictures/堆/堆排序4.png)
![堆排序4](./pictures/堆/堆排序4.png)
取出第五个元素并堆化:
![堆排序5](pictures/堆/堆排序5.png)
![堆排序5](./pictures/堆/堆排序5.png)
取出第六个元素并堆化:
![堆排序6](pictures/堆/堆排序6.png)
![堆排序6](./pictures/堆/堆排序6.png)
堆排序完成!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -328,7 +328,7 @@ HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格
![一个电子邮件被发送的过程](https://img-blog.csdnimg.cn/img_convert/b16da4d4fea63de5fce53f54973967d7.png)
<p style="text-align:right;font-size:12px">https://www.campaignmonitor.com/resources/knowledge-base/what-is-the-code-that-makes-bcc-or-cc-operate-in-an-email/<p>
<p style="text-align:right;font-size:12px">https://www.campaignmonitor.com/resources/knowledge-base/what-is-the-code-that-makes-bcc-or-cc-operate-in-an-email/</p>
11. **搜索引擎** :搜索引擎Search Engine是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息在对信息进行组织和处理后为用户提供检索服务将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。

View File

@ -37,7 +37,7 @@ _如果文章有任何需要改善和完善的地方欢迎在评论区指出
_玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Windows 用于玩游戏,一台 Mac 用于平时日常开发和学习使用。_
![windows](images/windows.png)
![windows](./images/windows.png)
#### 1.2.2. Unix
@ -45,7 +45,7 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win
目前这款操作系统已经逐渐逐渐退出操作系统的舞台。
![unix](images/unix.png)
![unix](./images/unix.png)
#### 1.2.3. Linux
@ -55,13 +55,13 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win
>
> **很多人更倾向使用 “GNU/Linux” 一词来表达人们通常所说的 “Linux”。**
![linux](images/linux.png)
![linux](./images/linux.png)
#### 1.2.4. Mac OS
苹果自家的操作系统,编程体验和 Linux 相当,但是界面、软件生态以及用户体验各方面都要比 Linux 操作系统更好。
![macos](images/macos.png)
![macos](./images/macos.png)
### 1.3. 操作系统的内核Kernel
@ -132,7 +132,7 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win
- **Linux 本质是指 Linux 内核** 严格来讲Linux 这个词本身只表示 Linux 内核,单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以,就有了各种 Linux 发行版。
- **Linux 之父(林纳斯·本纳第克特·托瓦兹 Linus Benedict Torvalds)** 一个编程领域的传奇式人物,真大佬!我辈崇拜敬仰之楷模。他是 **Linux 内核** 的最早作者,随后发起了这个开源项目,担任 Linux 内核的首要架构师。他还发起了 Git 这个开源项目,并为主要的开发者。
![Linux](images/Linux之父.png)
![Linux](./images/Linux之父.png)
### 2.2. Linux 诞生
@ -142,7 +142,7 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win
1991 年Linus Torvalds 开源了 Linux 内核。Linux 以一只可爱的企鹅作为标志,象征着敢作敢为、热爱生活。
![OPINION: Make the switch to a Linux operating system | Opinion ...](images/Linux-Logo.png)
![OPINION: Make the switch to a Linux operating system | Opinion ...](./images/Linux-Logo.png)
### 2.3. 常见 Linux 发行版本有哪些?
@ -182,7 +182,7 @@ Linux 的发行版本可以大体分为两类:
- **inode** :记录文件的属性信息,可以使用 stat 命令查看 inode 信息。
- **block** :实际文件的内容,如果一个文件大于一个块时候,那么将占用多个 block但是一个块只能存放一个文件。因为数据是由 inode 指向的,如果有两个文件的数据存放在同一个块中,就会乱套了)
![文件inode信息](images/文件inode信息.png)
![文件inode信息](./images/文件inode信息.png)
### 3.3. Linux 文件类型
@ -203,7 +203,7 @@ Linux 支持很多文件类型,其中非常重要的文件类型有: **普通
**Linux 的目录结构如下:**
Linux 文件系统的结构层次鲜明,就像一棵倒立的树,最顶层是其根目录:
![Linux的目录结构](images/Linux目录树.png)
![Linux的目录结构](./images/Linux目录树.png)
**常见目录说明:**
@ -287,11 +287,11 @@ Linux 中的打包文件一般是以.tar 结尾的,压缩的命令一般是以
示例:在随意某个目录下`ls -l`
![](images/Linux权限命令.png)
![](./images/Linux权限命令.png)
第一列的内容的信息解释如下:
![](images/Linux权限解读.png)
![](./images/Linux权限解读.png)
> 下面将详细讲解文件的类型、Linux 中权限以及文件有所有者、所在组、其它组具体是什么?
@ -343,7 +343,7 @@ Linux 中的打包文件一般是以.tar 结尾的,压缩的命令一般是以
**`chmod u=rwx,g=rw,o=r aaa.txt`** 或者 **`chmod 764 aaa.txt`**
![](images/修改文件权限.png)
![](./images/修改文件权限.png)
**补充一个比较常用的东西:**

View File

@ -26,8 +26,6 @@ tag:
面试官顶着蓬松的假发向我走来,只见他一手拿着厚重的 Thinkpad ,一手提着他那淡黄的长裙。
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/ceeb653ely1gd8wj5evc4j20i00n0dh0.jpg" height="300"></img>
### 1.1 什么是操作系统?
👨‍💻**面试官** 先来个简单问题吧!**什么是操作系统?**

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -48,7 +48,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
作为暖男一号,我给大家画了一个草图。
![正常缓存处理流程](images/redis-all/缓存的处理流程.png)
![正常缓存处理流程](./images/redis-all/cache-process.png)
简单来说就是:
@ -420,7 +420,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
- 文件事件分派器(将 socket 关联到相应的事件处理器)
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
![](images/redis-all/redis事件处理器.png)
![](./images/redis-all/redis事件处理器.png)
<p style="text-align:right; font-size:14px; color:gray">《Redis设计与实现12章》</p>
@ -428,7 +428,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
虽然说 Redis 是单线程模型,但是,实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
![redis4.0 more thread](images/redis-all/redis4.0-more-thread.png)
![redis4.0 more thread](./images/redis-all/redis4.0-more-thread.png)
不过Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主处理之外的其他线程来“异步处理”。
@ -494,7 +494,7 @@ OK
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
![redis过期字典](images/redis-all/redis过期时间.png)
![redis过期字典](./images/redis-all/redis过期时间.png)
过期字典是存储在 redisDb 这个结构里的:
@ -712,7 +712,7 @@ ERR EXEC without MULTI
Redis 官网相关介绍 [https://redis.io/topics/transactions](https://redis.io/topics/transactions) 如下:
![redis事务](images/redis-all/redis事务.png)
![redis事务](./images/redis-all/redis事务.png)
但是Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性: **1. 原子性****2. 隔离性****3. 持久性****4. 一致性**。
@ -725,7 +725,7 @@ Redis 官网相关介绍 [https://redis.io/topics/transactions](https://redis.io
Redis 官网也解释了自己为啥不支持回滚。简单来说就是 Redis 开发者们觉得没必要支持回滚这样更简单便捷并且性能更好。Redis 开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。
![redis roll back](images/redis-all/redis-rollBack.png)
![redis roll back](./images/redis-all/redis-rollBack.png)
你可以将 Redis 中的事务就理解为 **Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。**
@ -758,7 +758,8 @@ Redis 5.0 新增加的一个数据结构 `Stream` 可以用来做消息队列,
如下图所示,用户的请求最终都要跑到数据库中查询一遍。
![缓存穿透情况](./images/redis-all/缓存穿透情况.png)
![缓存穿透情况](https://img-blog.csdnimg.cn/6358650a9bf742838441d636430c90b9.png)
#### 有哪些解决办法?
@ -801,7 +802,7 @@ public Object getObjectInclNullById(Integer id) {
加入布隆过滤器之后的缓存处理流程图如下。
![image](images/redis-all/加入布隆过滤器后的缓存处理流程.png)
![](./images/redis-all/加入布隆过滤器后的缓存处理流程.png)
但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: **布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。**

View File

@ -36,7 +36,7 @@ root@eaf70fc620cb:/apache-zookeeper-3.5.8-bin# cd bin
如果你看到控制台成功打印出如下信息的话,说明你已经成功连接 ZooKeeper 服务。
![](images/连接ZooKeeper服务.png)
![](./images/连接ZooKeeper服务.png)
### 2.3. 常用命令演示
@ -162,7 +162,7 @@ numChildren = 1
Curator 是Netflix公司开源的一套 ZooKeeper Java客户端框架相比于 Zookeeper 自带的客户端 zookeeper 来说Curator 的封装更加完善,各种 API 都可以比较方便地使用。
![](images/curator.png)
![](./images/curator.png)
下面我们就来简单地演示一下 Curator 的使用吧!

View File

@ -79,7 +79,7 @@ ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都
从下图可以更直观地看出ZooKeeper 节点路径标识方式和 Unix 文件系统路径非常相似,都是由一系列使用斜杠"/"进行分割的路径表示,开发人员可以向这个节点中写入数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。
![ZooKeeper 数据模型](images/znode-structure.png)
![ZooKeeper 数据模型](./images/znode-structure.png)
### 3.2. znode数据节点
@ -172,7 +172,7 @@ ZooKeeper 采用 ACLAccessControlLists策略来进行权限控制类似
Watcher事件监听器是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher并且在一些特定事件触发的时候ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
![watcher机制](images/watche机制.png)
![watcher机制](./images/watche机制.png)
_破音非常有用的一个特性都拿出小本本记好了后面用到 ZooKeeper 基本离不开 Watcher事件监听器机制。_
@ -188,7 +188,7 @@ Session 有一个属性叫做:`sessionTimeout` `sessionTimeout` 代表会
为了保证高可用,最好是以集群形态来部署 ZooKeeper这样只要集群中大部分机器是可用的能够容忍一定的机器故障那么 ZooKeeper 本身仍然是可用的。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。ZooKeeper 官方提供的架构图就是一个 ZooKeeper 集群整体对外提供服务。
![](images/zookeeper集群.png)
![](./images/zookeeper集群.png)
上图中每一个 Server 代表一个安装 ZooKeeper 服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 ZAB 协议ZooKeeper Atomic Broadcast来保持数据的一致性。
@ -198,7 +198,7 @@ Session 有一个属性叫做:`sessionTimeout` `sessionTimeout` 代表会
但是,在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三种角色。如下图所示
![](images/zookeeper集群中的角色.png)
![](./images/zookeeper集群中的角色.png)
ZooKeeper 集群中的所有机器通过一个 **Leader 选举过程** 来选定一台称为 “**Leader**” 的机器Leader 既可以为客户端提供写服务又能提供读服务。除了 Leader 外,**Follower** 和 **Observer** 都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。

View File

@ -11,4 +11,4 @@
- [搞懂异地多活,看这篇就够了](https://mp.weixin.qq.com/s/T6mMDdtTfBuIiEowCpqu6Q)
- [四步构建异地多活](https://mp.weixin.qq.com/s/hMD-IS__4JE5_nQhYPYSTg)
- [《从零开始学架构》— 28 | 业务高可用的保障:异地多活架构](http://gk.link/a/10pKZ)
- [《从零开始学架构》— 28 | 业务高可用的保障:异地多活架构](http://gk.link/a/10pKZ)

View File

@ -1,4 +1,7 @@
# 超时&重试机制
---
title: 超时&重试机制
category: 高可用
---
**一旦用户的请求超过某个时间得不到响应就结束此次请求并抛出异常。** 如果不进行超时设置可能会导致请求响应速度慢,甚至导致请求堆积进而让系统无法在处理请求。

View File

@ -427,7 +427,7 @@ emmm是不是有一点复杂🤣看英文图片和英文文档的时候就
首先,在最上面的那一块就是我刚刚讲的你现在可以直接 **把 `ConsumerQueue` 理解为 `Queue`**
在图中最左边说明了 <font color = red>红色方块 </font> 代表被写入的消息,虚线方块代表等待被写入的。左边的生产者发送消息会指定 `Topic``QueueId` 和具体消息内容,而在 `Broker` 中管你是哪门子消息,他直接 **全部顺序存储到了 CommitLog**。而根据生产者指定的 `Topic``QueueId` 将这条消息本身在 `CommitLog` 的偏移(offset)消息本身大小和tag的hash值存入对应的 `ConsumeQueue` 索引文件中。而在每个队列中都保存了 `ConsumeOffset` 即每个消费者组的消费位置(我在架构那里提到了,忘了的同学可以回去看一下),而消费者拉取消息进行消费的时候只需要根据 `ConsumeOffset` 获取下一个未被消费的消息就行了。
在图中最左边说明了红色方块代表被写入的消息,虚线方块代表等待被写入的。左边的生产者发送消息会指定 `Topic``QueueId` 和具体消息内容,而在 `Broker` 中管你是哪门子消息,他直接 **全部顺序存储到了 CommitLog**。而根据生产者指定的 `Topic``QueueId` 将这条消息本身在 `CommitLog` 的偏移(offset)消息本身大小和tag的hash值存入对应的 `ConsumeQueue` 索引文件中。而在每个队列中都保存了 `ConsumeOffset` 即每个消费者组的消费位置(我在架构那里提到了,忘了的同学可以回去看一下),而消费者拉取消息进行消费的时候只需要根据 `ConsumeOffset` 获取下一个未被消费的消息就行了。
上述就是我对于整个消息存储架构的大概理解(这里不涉及到一些细节讨论,比如稀疏索引等等问题),希望对你有帮助。

View File

@ -3,7 +3,7 @@ icon: creative
title: JavaGuideJava学习&&面试指南)
---
<div align="center">
<div style="text-align:center">
<p>
<a href="https://www.yuque.com/docs/share/8a30ffb5-83f3-40f9-baf9-38de68b906dc">
<img src="../media/sponsor/xingqiu.png" style="margin: 0 auto; width: 850px;" />

View File

@ -67,7 +67,7 @@ REST 即 **REpresentational State Transfer** 的缩写。这个词组的翻译
举个例子。我们选中 `Controller` 中的某个请求对应的方法右击,你会发现多了几个可选项。当你选择`Generate & Copy Full URL`的话就可以把整个请求的路径直接复制下来。eg`http://localhost:9333/api/users?pageNum=1&pageSize=1`
![](pictures/RestfulToolkit3.png)
![](./pictures/RestfulToolkit3.png)
#### 将 Java 类转换为对应的 JSON 格式
@ -75,7 +75,7 @@ REST 即 **REpresentational State Transfer** 的缩写。这个词组的翻译
我们选中的某个类对应的方法然后右击,你会发现多了几个可选项。
![](pictures/RestfulToolkit4.png)
![](./pictures/RestfulToolkit4.png)
当我们选择`Convert to JSON`的话,你会得到如下 json 类型的数据:

View File

@ -213,13 +213,13 @@ static int hash(int h) {
所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
![jdk1.8之前的内部结构-HashMap](images/jdk1.8之前的内部结构-HashMap.png)
![jdk1.8之前的内部结构-HashMap](./images/jdk1.8之前的内部结构-HashMap.png)
#### JDK1.8 之后
相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8将链表转换成红黑树前会判断如果当前数组的长度小于 64那么会选择先进行数组扩容而不是转换为红黑树将链表转化为红黑树以减少搜索时间。
![jdk1.8之后的内部结构-HashMap](images/jdk1.8之后的内部结构-HashMap.png)
![jdk1.8之后的内部结构-HashMap](./images/jdk1.8之后的内部结构-HashMap.png)
> TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。

View File

@ -393,11 +393,11 @@ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话, `Thread`内部都是使用仅有的那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象value 就是 `ThreadLocal` 对象调用`set`方法设置的值。
![ThreadLocal数据结构](images/threadlocal数据结构.png)
![ThreadLocal数据结构](./images/threadlocal数据结构.png)
`ThreadLocalMap``ThreadLocal`的静态内部类。
![ThreadLocal内部类](images/ThreadLocal内部类.png)
![ThreadLocal内部类](./images/ThreadLocal内部类.png)
### 3.4. ThreadLocal 内存泄露问题
@ -761,7 +761,7 @@ public void execute(Runnable command) {
通过下图可以更好的对上面这 3 步做一个展示,下图是我为了省事直接从网上找到,原地址不明。
![图解线程池实现原理](images/java-thread-pool-summary/图解线程池实现原理.png)
![图解线程池实现原理](./images/java-thread-pool-summary/图解线程池实现原理.png)
现在,让我们在回到 4.6 节我们写的 Demo 现在是不是很容易就可以搞懂它的原理了呢?

View File

@ -59,7 +59,7 @@ public class ScheduledThreadPoolExecutor
implements ScheduledExecutorService
```
![任务的执行相关接口](images/java-thread-pool-summary/任务的执行相关接口.png)
![任务的执行相关接口](./images/java-thread-pool-summary/任务的执行相关接口.png)
#### 3) 异步计算的结果(`Future`)
@ -69,7 +69,7 @@ public class ScheduledThreadPoolExecutor
### 2.3 Executor 框架的使用示意图
![Executor 框架的使用示意图](images/java-thread-pool-summary/Executor框架的使用示意图.png)
![Executor 框架的使用示意图](./images/java-thread-pool-summary/Executor框架的使用示意图.png)
1. **主线程首先要创建实现 `Runnable` 或者 `Callable` 接口的任务对象。**
2. **把创建完成的实现 `Runnable`/`Callable`接口的 对象直接交给 `ExecutorService` 执行**: `ExecutorService.executeRunnable command`)或者也可以把 `Runnable` 对象或`Callable` 对象提交给 `ExecutorService` 执行(`ExecutorService.submitRunnable task``ExecutorService.submitCallable <T> task`)。
@ -129,7 +129,7 @@ public class ScheduledThreadPoolExecutor
下面这张图可以加深你对线程池中各个参数的相互关系的理解图片来源《Java 性能调优实战》):
![线程池各个参数的关系](images/java-thread-pool-summary/线程池各个参数之间的关系.png)
![线程池各个参数的关系](./images/java-thread-pool-summary/线程池各个参数之间的关系.png)
**`ThreadPoolExecutor` 饱和策略定义:**
@ -160,7 +160,7 @@ public class ScheduledThreadPoolExecutor
> - **`CachedThreadPool``ScheduledThreadPool`** 允许创建的线程数量为 `Integer.MAX_VALUE` ,可能会创建大量线程,从而导致 OOM。
**方式一:通过`ThreadPoolExecutor`构造函数实现(推荐)**
![通过构造方法实现](images/java-thread-pool-summary/threadpoolexecutor构造函数.png)
![通过构造方法实现](./images/java-thread-pool-summary/threadpoolexecutor构造函数.png)
**方式二:通过 `Executor` 框架的工具类 `Executors` 来实现**
我们可以创建三种类型的 `ThreadPoolExecutor`
@ -171,7 +171,7 @@ public class ScheduledThreadPoolExecutor
对应 Executors 工具类中的方法如图所示:
![通过Executor 框架的工具类Executors来实现](images/java-thread-pool-summary/Executors工具类.png)
![通过Executor 框架的工具类Executors来实现](./images/java-thread-pool-summary/Executors工具类.png)
## 四 ThreadPoolExecutor 使用+原理分析
@ -693,7 +693,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5
`FixedThreadPool``execute()` 方法运行示意图该图片来源《Java 并发编程的艺术》):
![FixedThreadPool的execute()方法运行示意图](images/java-thread-pool-summary/FixedThreadPool.png)
![FixedThreadPool的execute()方法运行示意图](./images/java-thread-pool-summary/FixedThreadPool.png)
**上图说明:**
@ -743,7 +743,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5
#### 5.2.2 执行任务过程介绍
`SingleThreadExecutor` 的运行示意图该图片来源《Java 并发编程的艺术》):
![SingleThreadExecutor的运行示意图](images/java-thread-pool-summary/SingleThreadExecutor.png)
![SingleThreadExecutor的运行示意图](./images/java-thread-pool-summary/SingleThreadExecutor.png)
**上图说明** :
@ -787,7 +787,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5
#### 5.3.2 执行任务过程介绍
`CachedThreadPool``execute()` 方法的执行示意图该图片来源《Java 并发编程的艺术》):
![CachedThreadPool的execute()方法的执行示意图](images/java-thread-pool-summary/CachedThreadPool-execute.png)
![CachedThreadPool的execute()方法的执行示意图](./images/java-thread-pool-summary/CachedThreadPool-execute.png)
**上图说明:**
@ -818,7 +818,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5
### 6.2 运行机制
![ScheduledThreadPoolExecutor运行机制](images/java-thread-pool-summary/ScheduledThreadPoolExecutor机制.png)
![ScheduledThreadPoolExecutor运行机制](./images/java-thread-pool-summary/ScheduledThreadPoolExecutor机制.png)
**`ScheduledThreadPoolExecutor` 的执行主要分为两大部分:**
@ -833,7 +833,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5
### 6.3 ScheduledThreadPoolExecutor 执行周期任务的步骤
![ScheduledThreadPoolExecutor执行周期任务的步骤](images/java-thread-pool-summary/ScheduledThreadPoolExecutor执行周期任务步骤.png)
![ScheduledThreadPoolExecutor执行周期任务的步骤](./images/java-thread-pool-summary/ScheduledThreadPoolExecutor执行周期任务步骤.png)
1. 线程 1 从 `DelayQueue` 中获取已到期的 `ScheduledFutureTaskDelayQueue.take()`。到期任务是指 `ScheduledFutureTask`的 time 大于等于当前系统的时间;
2. 线程 1 执行这个 `ScheduledFutureTask`

View File

@ -1,371 +0,0 @@
# JAD 反编译
[jad](https://varaneckas.com/jad/)反编译工具,已经不再更新,且只支持 JDK1.4,但并不影响其强大的功能。
基本用法:`jad xxx.class`,会生成直接可读的 `xxx.jad` 文件。
## 自动拆装箱
对于基本类型和包装类型之间的转换,通过 xxxValue()和 valueOf()两个方法完成自动拆装箱,使用 jad 进行反编译可以看到该过程:
```java
public class Demo {
public static void main(String[] args) {
int x = new Integer(10); // 自动拆箱
Integer y = x; // 自动装箱
}
}
```
反编译后结果:
```java
public class Demo
{
public Demo(){}
public static void main(String args[])
{
int i = (new Integer(10)).intValue(); // intValue()拆箱
Integer integer = Integer.valueOf(i); // valueOf()装箱
}
}
```
## foreach 语法糖
在遍历迭代时可以 foreach 语法糖,对于数组类型直接转换成 for 循环:
```java
// 原始代码
int[] arr = {1, 2, 3, 4, 5};
for(int item: arr) {
System.out.println(item);
}
}
// 反编译后代码
int ai[] = {
1, 2, 3, 4, 5
};
int ai1[] = ai;
int i = ai1.length;
// 转换成for循环
for(int j = 0; j < i; j++)
{
int k = ai1[j];
System.out.println(k);
}
```
对于容器类的遍历会使用 iterator 进行迭代:
```java
import java.io.PrintStream;
import java.util.*;
public class Demo
{
public Demo() {}
public static void main(String args[])
{
ArrayList arraylist = new ArrayList();
arraylist.add(Integer.valueOf(1));
arraylist.add(Integer.valueOf(2));
arraylist.add(Integer.valueOf(3));
Integer integer;
// 使用的for循环+Iterator类似于链表迭代
// for (ListNode cur = head; cur != null; System.out.println(cur.val)){
// cur = cur.next;
// }
for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(integer))
integer = (Integer)iterator.next();
}
}
```
## Arrays.asList(T...)
熟悉 Arrays.asList(T...)用法的小伙伴都应该知道asList()方法传入的参数不能是基本类型的数组,必须包装成包装类型再使用,否则对应生成的列表的大小永远是 1
```java
import java.util.*;
public class Demo {
public static void main(String[] args) {
int[] arr1 = {1, 2, 3};
Integer[] arr2 = {1, 2, 3};
List lists1 = Arrays.asList(arr1);
List lists2 = Arrays.asList(arr2);
System.out.println(lists1.size()); // 1
System.out.println(lists2.size()); // 3
}
}
```
从反编译结果来解释,为什么传入基本类型的数组后,返回的 List 大小是 1
```java
// 反编译后文件
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
public class Demo
{
public Demo() {}
public static void main(String args[])
{
int ai[] = {
1, 2, 3
};
// 使用包装类型全部元素由int包装为Integer
Integer ainteger[] = {
Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)
};
// 注意这里被反编译成二维数组而且是一个1行三列的二维数组
// list.size()当然返回1
List list = Arrays.asList(new int[][] { ai });
List list1 = Arrays.asList(ainteger);
System.out.println(list.size());
System.out.println(list1.size());
}
}
```
从上面结果可以看到,传入基本类型的数组后,会被转换成一个二维数组,而且是**new int\[1]\[arr.length]**这样的数组,调用 list.size()当然返回 1。
## 注解
Java 中的类、接口、枚举、注解都可以看做是类类型。使用 jad 来看一下@interface 被转换成什么:
```java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Foo{
String[] value();
boolean bar();
}
```
查看反编译代码可以看出:
- 自定义的注解类 Foo 被转换成接口 Foo并且继承 Annotation 接口
- 原来自定义接口中的 value()和 bar()被转换成抽象方法
```java
import java.lang.annotation.Annotation;
public interface Foo
extends Annotation
{
public abstract String[] value();
public abstract boolean bar();
}
```
注解通常和反射配合使用,而且既然自定义的注解最终被转换成接口,注解中的属性被转换成接口中的抽象方法,那么通过反射之后拿到接口实例,在通过接口实例自然能够调用对应的抽象方法:
```java
import java.util.Arrays;
@Foo(value={"sherman", "decompiler"}, bar=true)
public class Demo{
public static void main(String[] args) {
Foo foo = Demo.class.getAnnotation(Foo.class);
System.out.println(Arrays.toString(foo.value())); // [sherman, decompiler]
System.out.println(foo.bar()); // true
}
}
```
## 枚举
通过 jad 反编译可以很好地理解枚举类。
### 空枚举
先定义一个空的枚举类:
```java
public enum DummyEnum {
}
```
使用 jad 反编译查看结果:
- 自定义枚举类被转换成 final 类,并且继承 Enum
- 提供了两个参数nameodinal的私有构造器并且调用了父类的构造器。注意即使没有提供任何参数也会有该构造器其中 name 就是枚举实例的名称odinal 是枚举实例的索引号
- 初始化了一个 private static final 自定义类型的空数组 **\$VALUES**
- 提供了两个 public static 方法:
- values()方法通过 clone()方法返回内部\$VALUES 的浅拷贝。这个方法结合私有构造器可以完美实现单例模式,想一想 values()方法是不是和单例模式中 getInstance()方法功能类似
- valueOf(String s):调用父类 Enum 的 valueOf 方法并强转返回
```java
public final class DummyEnum extends Enum
{
// 功能和单例模式的getInstance()方法相同
public static DummyEnum[] values()
{
return (DummyEnum[])$VALUES.clone();
}
// 调用父类的valueOf方法并强转返回
public static DummyEnum valueOf(String s)
{
return (DummyEnum)Enum.valueOf(DummyEnum, s);
}
// 默认提供一个私有的两个参数的构造器并调用父类Enum的构造器
private DummyEnum(String s, int i)
{
super(s, i);
}
// 初始化一个private static final的本类空数组
private static final DummyEnum $VALUES[] = new DummyEnum[0];
}
```
### 包含抽象方法的枚举
枚举类中也可以包含抽象方法,但是必须定义枚举实例并且立即重写抽象方法,就像下面这样:
```java
public enum DummyEnum {
DUMMY1 {
public void dummyMethod() {
System.out.println("[1]: implements abstract method in enum class");
}
},
DUMMY2 {
public void dummyMethod() {
System.out.println("[2]: implements abstract method in enum class");
}
};
abstract void dummyMethod();
}
```
再来反编译看看有哪些变化:
- 原来 final class 变成了 abstract class这很好理解有抽象方法的类自然是抽象类
- 多了两个 public static final 的成员 DUMMY1、DUMMY2这两个实例的初始化过程被放到了 static 代码块中,并且实例过程中直接重写了抽象方法,类似于匿名内部类的形式。
- 数组 **\$VALUES[]** 初始化时放入枚举实例
还有其它变化么?
在反编译后的 DummyEnum 类中,是存在抽象方法的,而枚举实例在静态代码块中初始化过程中重写了抽象方法。在 Java 中,抽象方法和抽象方法重写同时放在一个类中,只能通过内部类形式完成。因此上面第二点应该说成就是以内部类形式初始化。
可以看一下 DummyEnum.class 存放的位置,应该多了两个文件:
- DummyEnum\$1.class
- DummyEnum\$2.class
Java 中.class 文件出现 $ 符号表示有内部类存在就像OutClass$InnerClass这两个文件出现也应证了上面的匿名内部类初始化的说法。
```java
import java.io.PrintStream;
public abstract class DummyEnum extends Enum
{
public static DummyEnum[] values()
{
return (DummyEnum[])$VALUES.clone();
}
public static DummyEnum valueOf(String s)
{
return (DummyEnum)Enum.valueOf(DummyEnum, s);
}
private DummyEnum(String s, int i)
{
super(s, i);
}
// 抽象方法
abstract void dummyMethod();
// 两个pubic static final实例
public static final DummyEnum DUMMY1;
public static final DummyEnum DUMMY2;
private static final DummyEnum $VALUES[];
// static代码块进行初始化
static
{
DUMMY1 = new DummyEnum("DUMMY1", 0) {
public void dummyMethod()
{
System.out.println("[1]: implements abstract method in enum class");
}
}
;
DUMMY2 = new DummyEnum("DUMMY2", 1) {
public void dummyMethod()
{
System.out.println("[2]: implements abstract method in enum class");
}
}
;
// 对本类数组进行初始化
$VALUES = (new DummyEnum[] {
DUMMY1, DUMMY2
});
}
}
```
### 正常的枚举类
实际开发中枚举类通常的形式是有两个参数int codeSring msg的构造器可以作为状态码进行返回。Enum 类实际上也是提供了包含两个参数且是 protected 的构造器,这里为了避免歧义,将枚举类的构造器设置为三个,使用 jad 反编译:
最大的变化是:现在的 private 构造器从 2 个参数变成 5 个,而且在内部仍然将前两个参数通过 super 传递给父类,剩余的三个参数才是真正自己提供的参数。可以想象,如果自定义的枚举类只提供了一个参数,最终生成底层代码中 private 构造器应该有三个参数,前两个依然通过 super 传递给父类。
```java
public final class CustomEnum extends Enum
{
public static CustomEnum[] values()
{
return (CustomEnum[])$VALUES.clone();
}
public static CustomEnum valueOf(String s)
{
return (CustomEnum)Enum.valueOf(CustomEnum, s);
}
private CustomEnum(String s, int i, int j, String s1, Object obj)
{
super(s, i);
code = j;
msg = s1;
data = obj;
}
public static final CustomEnum FIRST;
public static final CustomEnum SECOND;
public static final CustomEnum THIRD;
private int code;
private String msg;
private Object data;
private static final CustomEnum $VALUES[];
static
{
FIRST = new CustomEnum("FIRST", 0, 10010, "first", Long.valueOf(100L));
SECOND = new CustomEnum("SECOND", 1, 10020, "second", "Foo");
THIRD = new CustomEnum("THIRD", 2, 10030, "third", new Object());
$VALUES = (new CustomEnum[] {
FIRST, SECOND, THIRD
});
}
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,402 +0,0 @@
# 手把手教你定位常见 Java 性能问题
> 本文来自木木匠投稿。
## 概述
性能优化一向是后端服务优化的重点,但是线上性能故障问题不是经常出现,或者受限于业务产品,根本就没办法出现性能问题,包括笔者自己遇到的性能问题也不多,所以为了提前储备知识,当出现问题的时候不会手忙脚乱,我们本篇文章来模拟下常见的几个 Java 性能故障,来学习怎么去分析和定位。
## 预备知识
既然是定位问题,肯定是需要借助工具,我们先了解下需要哪些工具可以帮忙定位问题。
**top 命令**
`top`命令使我们最常用的 Linux 命令之一,它可以实时的显示当前正在执行的进程的 CPU 使用率,内存使用率等系统信息。`top -Hp pid` 可以查看线程的系统资源使用情况。
**vmstat 命令**
vmstat 是一个指定周期和采集次数的虚拟内存检测工具可以统计内存CPUswap 的使用情况,它还有一个重要的常用功能,用来观察进程的上下文切换。字段说明如下:
- r: 运行队列中进程数量(当数量大于 CPU 核数表示有阻塞的线程)
- b: 等待 IO 的进程数量
- swpd: 使用虚拟内存大小
- free: 空闲物理内存大小
- buff: 用作缓冲的内存大小(内存和硬盘的缓冲区)
- cache: 用作缓存的内存大小CPU 和内存之间的缓冲区)
- si: 每秒从交换区写到内存的大小,由磁盘调入内存
- so: 每秒写入交换区的内存大小,由内存调入磁盘
- bi: 每秒读取的块数
- bo: 每秒写入的块数
- in: 每秒中断数,包括时钟中断。
- cs: 每秒上下文切换数。
- us: 用户进程执行时间百分比(user time)
- sy: 内核系统进程执行时间百分比(system time)
- wa: IO 等待时间百分比
- id: 空闲时间百分比
**pidstat 命令**
pidstat 是 Sysstat 中的一个组件,也是一款功能强大的性能监测工具,`top``vmstat` 两个命令都是监测进程的内存、CPU 以及 I/O 使用情况,而 pidstat 命令可以检测到线程级别的。`pidstat`命令线程切换字段说明如下:
- UID :被监控任务的真实用户 ID。
- TGID :线程组 ID。
- TID线程 ID。
- cswch/s主动切换上下文次数这里是因为资源阻塞而切换线程比如锁等待等情况。
- nvcswch/s被动切换上下文次数这里指 CPU 调度切换了线程。
**jstack 命令**
jstack 是 JDK 工具命令,它是一种线程堆栈分析工具,最常用的功能就是使用 `jstack pid` 命令查看线程的堆栈信息,也经常用来排除死锁情况。
**jstat 命令**
它可以检测 Java 程序运行的实时情况,包括堆内存信息和垃圾回收信息,我们常常用来查看程序垃圾回收情况。常用的命令是`jstat -gc pid`。信息字段说明如下:
- S0C年轻代中 To Survivor 的容量(单位 KB
- S1C年轻代中 From Survivor 的容量(单位 KB
- S0U年轻代中 To Survivor 目前已使用空间(单位 KB
- S1U年轻代中 From Survivor 目前已使用空间(单位 KB
- EC年轻代中 Eden 的容量(单位 KB
- EU年轻代中 Eden 目前已使用空间(单位 KB
- OC老年代的容量单位 KB
- OU老年代目前已使用空间单位 KB
- MC元空间的容量单位 KB
- MU元空间目前已使用空间单位 KB
- YGC从应用程序启动到采样时年轻代中 gc 次数;
- YGCT从应用程序启动到采样时年轻代中 gc 所用时间 (s)
- FGC从应用程序启动到采样时 老年代Full Gcgc 次数;
- FGCT从应用程序启动到采样时 老年代代Full Gcgc 所用时间 (s)
- GCT从应用程序启动到采样时 gc 用的总时间 (s)。
**jmap 命令**
jmap 也是 JDK 工具命令,他可以查看堆内存的初始化信息以及堆内存的使用情况,还可以生成 dump 文件来进行详细分析。查看堆内存情况命令`jmap -heap pid`
**mat 内存工具**
MAT(Memory Analyzer Tool)工具是 eclipse 的一个插件(MAT 也可以单独使用),它分析大内存的 dump 文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用 OQL 对象查询,以及可以很方便的找出对象 GC Roots 的相关信息。
**idea 中也有这么一个插件,就是 JProfiler**。
相关阅读:[《性能诊断利器 JProfiler 快速入门和最佳实践》](https://segmentfault.com/a/1190000017795841)
## 模拟环境准备
基础环境 jdk1.8,采用 SpringBoot 框架来写几个接口来触发模拟场景,首先是模拟 CPU 占满情况
### CPU 占满
模拟 CPU 占满还是比较简单,直接写一个死循环计算消耗 CPU 即可。
```java
/**
* 模拟CPU占满
*/
@GetMapping("/cpu/loop")
public void testCPULoop() throws InterruptedException {
System.out.println("请求cpu死循环");
Thread.currentThread().setName("loop-thread-cpu");
int num = 0;
while (true) {
num++;
if (num == Integer.MAX_VALUE) {
System.out.println("reset");
}
num = 0;
}
}
```
请求接口地址测试`curl localhost:8080/cpu/loop`,发现 CPU 立马飙升到 100%
![](./images/3be5a280b0f5499a80c706c8e5da2a4f-1.png)
通过执行`top -Hp 32805` 查看 Java 线程情况
![](./images/3d8d5ffd3ada43fb86ef54b05408c656-1.png)
执行 `printf '%x' 32826` 获取 16 进制的线程 id用于`dump`信息查询,结果为 `803a`。最后我们执行`jstack 32805 |grep -A 20 803a`来查看下详细的`dump`信息。
![](./images/1fb751b0d78b4a3b8d0f528598ae885d-1.png)
这里`dump`信息直接定位出了问题方法以及代码行,这就定位出了 CPU 占满的问题。
### 内存泄露
模拟内存泄漏借助了 ThreadLocal 对象来完成ThreadLocal 是一个线程私有变量,可以绑定到线程上,在整个线程的生命周期都会存在,但是由于 ThreadLocal 的特殊性ThreadLocal 是基于 ThreadLocalMap 实现的ThreadLocalMap 的 Entry 继承 WeakReference而 Entry 的 Key 是 WeakReference 的封装,换句话说 Key 就是弱引用,弱引用在下次 GC 之后就会被回收,如果 ThreadLocal 在 set 之后不进行后续的操作,因为 GC 会把 Key 清除掉,但是 Value 由于线程还在存活,所以 Value 一直不会被回收,最后就会发生内存泄漏。
```Java
/**
* 模拟内存泄漏
*/
@GetMapping(value = "/memory/leak")
public String leak() {
System.out.println("模拟内存泄漏");
ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();
localVariable.set(new Byte[4096 * 1024]);// 为线程添加变量
return "ok";
}
```
我们给启动加上堆内存大小限制,同时设置内存溢出的时候输出堆栈快照并输出日志。
```bash
java -jar -Xms500m -Xmx500m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heaplog.log analysis-demo-0.0.1-SNAPSHOT.jar
```
启动成功后我们循环执行 100 次,`for i in {1..500}; do curl localhost:8080/memory/leak;done`,还没执行完毕,系统已经返回 500 错误了。查看系统日志出现了如下异常:
```
java.lang.OutOfMemoryError: Java heap space
```
我们用`jstat -gc pid` 命令来看看程序的 GC 情况。
![](./images/e9bf831860f442a3a992eef64ebb6a50-1.png)
很明显,内存溢出了,堆内存经过 45 次 Full Gc 之后都没释放出可用内存,这说明当前堆内存中的对象都是存活的,有 GC Roots 引用,无法回收。那是什么原因导致内存溢出呢?是不是我只要加大内存就行了呢?如果是普通的内存溢出也许扩大内存就行了,但是如果是内存泄漏的话,扩大的内存不一会就会被占满,所以我们还需要确定是不是内存泄漏。我们之前保存了堆 Dump 文件,这个时候借助我们的 MAT 工具来分析下。导入工具选择`Leak Suspects Report`,工具直接就会给你列出问题报告。
![](./images/392e4090c0094657ae29af030d3646e3-1.png)
这里已经列出了可疑的 4 个内存泄漏问题,我们点击其中一个查看详情。
![](./images/53fd3ee9a1a0448ca1878e865f4e5f96-1.png)
这里已经指出了内存被线程占用了接近 50M 的内存,占用的对象就是 ThreadLocal。如果想详细的通过手动去分析的话可以点击`Histogram`,查看最大的对象占用是谁,然后再分析它的引用关系,即可确定是谁导致的内存溢出。
![](./images/ba07b0fee1754ffc943e546a18a3907e-1.png)
上图发现占用内存最大的对象是一个 Byte 数组,我们看看它到底被那个 GC Root 引用导致没有被回收。按照上图红框操作指引,结果如下图:
![](./images/0605fbf554814a23b80f6351408598be-1.png)
我们发现 Byte 数组是被线程对象引用的图中也标明Byte 数组对像的 GC Root 是线程,所以它是不会被回收的,展开详细信息查看,我们发现最终的内存占用对象是被 ThreadLocal 对象占据了。这也和 MAT 工具自动帮我们分析的结果一致。
### 死锁
死锁会导致耗尽线程资源占用内存表现就是内存占用升高CPU 不一定会飙升(看场景决定),如果是直接 new 线程,会导致 JVM 内存被耗尽,报无法创建线程的错误,这也是体现了使用线程池的好处。
```java
ExecutorService service = new ThreadPoolExecutor(4, 10,
0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1024),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
/**
* 模拟死锁
*/
@GetMapping("/cpu/test")
public String testCPU() throws InterruptedException {
System.out.println("请求cpu");
Object lock1 = new Object();
Object lock2 = new Object();
service.submit(new DeadLockThread(lock1, lock2), "deadLookThread-" + new Random().nextInt());
service.submit(new DeadLockThread(lock2, lock1), "deadLookThread-" + new Random().nextInt());
return "ok";
}
public class DeadLockThread implements Runnable {
private Object lock1;
private Object lock2;
public DeadLockThread1(Object lock1, Object lock2) {
this.lock1 = lock1;
this.lock2 = lock2;
}
@Override
public void run() {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName()+"get lock2 and wait lock1");
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName()+"get lock1 and lock2 ");
}
}
}
}
```
我们循环请求接口 2000 次,发现不一会系统就出现了日志错误,线程池和队列都满了,由于我选择的当队列满了就拒绝的策略,所以系统直接抛出异常。
```
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@2760298 rejected from java.util.concurrent.ThreadPoolExecutor@7ea7cd51[Running, pool size = 10, active threads = 10, queued tasks = 1024, completed tasks = 846]
```
通过`ps -ef|grep java`命令找出 Java 进程 pid执行`jstack pid` 即可出现 java 线程堆栈信息,这里发现了 5 个死锁,我们只列出其中一个,很明显线程`pool-1-thread-2`锁住了`0x00000000f8387d88`等待`0x00000000f8387d98`锁,线程`pool-1-thread-1`锁住了`0x00000000f8387d98`等待锁`0x00000000f8387d88`,这就产生了死锁。
```JAVA
Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":
at top.luozhou.analysisdemo.controller.DeadLockThread2.run(DeadLockThread.java:30)
- waiting to lock <0x00000000f8387d98> (a java.lang.Object)
- locked <0x00000000f8387d88> (a java.lang.Object)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"pool-1-thread-1":
at top.luozhou.analysisdemo.controller.DeadLockThread1.run(DeadLockThread.java:30)
- waiting to lock <0x00000000f8387d88> (a java.lang.Object)
- locked <0x00000000f8387d98> (a java.lang.Object)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Found 5 deadlocks.
```
### 线程频繁切换
上下文切换会导致将大量 CPU 时间浪费在寄存器、内核栈以及虚拟内存的保存和恢复上,导致系统整体性能下降。当你发现系统的性能出现明显的下降时候,需要考虑是否发生了大量的线程上下文切换。
```java
@GetMapping(value = "/thread/swap")
public String theadSwap(int num) {
System.out.println("模拟线程切换");
for (int i = 0; i < num; i++) {
new Thread(new ThreadSwap1(new AtomicInteger(0)),"thread-swap"+i).start();
}
return "ok";
}
public class ThreadSwap1 implements Runnable {
private AtomicInteger integer;
public ThreadSwap1(AtomicInteger integer) {
this.integer = integer;
}
@Override
public void run() {
while (true) {
integer.addAndGet(1);
Thread.yield(); //让出CPU资源
}
}
}
```
这里我创建多个线程去执行基础的原子+1 操作,然后让出 CPU 资源,理论上 CPU 就会去调度别的线程,我们请求接口创建 100 个线程看看效果如何,`curl localhost:8080/thread/swap?num=100`。接口请求成功后,我们执行 `vmstat 1 10`,表示每 1 秒打印一次,打印 10 次,线程切换采集结果如下:
```
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
101 0 128000 878384 908 468684 0 0 0 0 4071 8110498 14 86 0 0 0
100 0 128000 878384 908 468684 0 0 0 0 4065 8312463 15 85 0 0 0
100 0 128000 878384 908 468684 0 0 0 0 4107 8207718 14 87 0 0 0
100 0 128000 878384 908 468684 0 0 0 0 4083 8410174 14 86 0 0 0
100 0 128000 878384 908 468684 0 0 0 0 4083 8264377 14 86 0 0 0
100 0 128000 878384 908 468688 0 0 0 108 4182 8346826 14 86 0 0 0
```
这里我们关注 4 个指标,`r`,`cs`,`us`,`sy`
**r=100**,说明等待的进程数量是 100线程有阻塞。
**cs=800 多万**,说明每秒上下文切换了 800 多万次,这个数字相当大了。
**us=14**,说明用户态占用了 14%的 CPU 时间片去处理逻辑。
**sy=86**,说明内核态占用了 86%的 CPU这里明显就是做上下文切换工作了。
我们通过`top`命令以及`top -Hp pid`查看进程和线程 CPU 情况,发现 Java 进程 CPU 占满了,但是线程 CPU 使用情况很平均,没有某一个线程把 CPU 吃满的情况。
```
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
87093 root 20 0 4194788 299056 13252 S 399.7 16.1 65:34.67 java
```
```
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
87189 root 20 0 4194788 299056 13252 R 4.7 16.1 0:41.11 java
87129 root 20 0 4194788 299056 13252 R 4.3 16.1 0:41.14 java
87130 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.51 java
87133 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.59 java
87134 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.95 java
```
结合上面用户态 CPU 只使用了 14%,内核态 CPU 占用了 86%,可以基本判断是 Java 程序线程上下文切换导致性能问题。
我们使用`pidstat`命令来看看 Java 进程内部的线程切换数据,执行`pidstat -p 87093 -w 1 10`,采集数据如下:
```
11:04:30 PM UID TGID TID cswch/s nvcswch/s Command
11:04:30 PM 0 - 87128 0.00 16.07 |__java
11:04:30 PM 0 - 87129 0.00 15.60 |__java
11:04:30 PM 0 - 87130 0.00 15.54 |__java
11:04:30 PM 0 - 87131 0.00 15.60 |__java
11:04:30 PM 0 - 87132 0.00 15.43 |__java
11:04:30 PM 0 - 87133 0.00 16.02 |__java
11:04:30 PM 0 - 87134 0.00 15.66 |__java
11:04:30 PM 0 - 87135 0.00 15.23 |__java
11:04:30 PM 0 - 87136 0.00 15.33 |__java
11:04:30 PM 0 - 87137 0.00 16.04 |__java
```
根据上面采集的信息,我们知道 Java 的线程每秒切换 15 次左右,正常情况下,应该是个位数或者小数。结合这些信息我们可以断定 Java 线程开启过多,导致频繁上下文切换,从而影响了整体性能。
**为什么系统的上下文切换是每秒 800 多万,而 Java 进程中的某一个线程切换才 15 次左右?**
系统上下文切换分为三种情况:
1、多任务在多任务环境中一个进程被切换出 CPU运行另外一个进程这里会发生上下文切换。
2、中断处理发生中断时硬件会切换上下文。在 vmstat 命令中是`in`
3、用户和内核模式切换当操作系统中需要在用户模式和内核模式之间进行转换时需要进行上下文切换,比如进行系统函数调用。
Linux 为每个 CPU 维护了一个就绪队列,将活跃进程按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。也就是 vmstat 命令中的`r`
那么,进程在什么时候才会被调度到 CPU 上运行呢?
- 进程执行完终止了,它之前使用的 CPU 会释放出来,这时再从就绪队列中拿一个新的进程来运行
- 为了保证所有进程可以得到公平调度CPU 时间被划分为一段段的时间片,这些时间片被轮流分配给各个进程。当某个进程时间片耗尽了就会被系统挂起,切换到其它等待 CPU 的进程运行。
- 进程在系统资源不足时,要等待资源满足后才可以运行,这时进程也会被挂起,并由系统调度其它进程运行。
- 当进程通过睡眠函数 sleep 主动挂起时,也会重新调度。
- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行。
- 发生硬件中断时CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。
结合我们之前的内容分析,阻塞的就绪队列是 100 左右,而我们的 CPU 只有 4 核,这部分原因造成的上下文切换就可能会相当高,再加上中断次数是 4000 左右和系统的函数调用等,整个系统的上下文切换到 800 万也不足为奇了。Java 内部的线程切换才 15 次,是因为线程使用`Thread.yield()`来让出 CPU 资源,但是 CPU 有可能继续调度该线程,这个时候线程之间并没有切换,这也是为什么内部的某个线程切换次数并不是非常大的原因。
## 总结
本文模拟了常见的性能问题场景,分析了如何定位 CPU100%、内存泄漏、死锁、线程频繁切换问题。分析问题我们需要做好两件事,第一,掌握基本的原理,第二,借助好工具。本文也列举了分析问题的常用工具和命令,希望对你解决问题有所帮助。当然真正的线上环境可能十分复杂,并没有模拟的环境那么简单,但是原理是一样的,问题的表现也是类似的,我们重点抓住原理,活学活用,相信复杂的线上问题也可以顺利解决。
## 参考
1、https://linux.die.net/man/1/pidstat
2、https://linux.die.net/man/8/vmstat
3、https://help.eclipse.org/2020-03/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html
4、https://www.linuxblogs.cn/articles/18120200.html
5、https://www.tutorialspoint.com/what-is-context-switching-in-operating-system

View File

@ -222,11 +222,11 @@ Codelf 提供了在线网站版本,网址:[https://unbug.github.io/codelf/](
我选择了 Java 编程语言,然后搜索了“序列化”这个关键词,然后它就返回了很多关于序列化的命名。
![](pictures/Codelf.png)
![](./pictures/Codelf.png)
并且Codelf 还提供了 VS code 插件,看这个评价,看来大家还是很喜欢这款命名工具的。
![](pictures/vscode-codelf.png)
![](./pictures/vscode-codelf.png)
## 相关阅读推荐

View File

@ -274,7 +274,7 @@ public class UserRegisterRequest {
这样我们的后端就可以直接把 json 格式的数据映射到我们的 `UserRegisterRequest` 类上。
![](images/spring-annotations/@RequestBody.png)
![](./images/spring-annotations/@RequestBody.png)
👉 需要注意的是:**一个请求方法只可以有一个`@RequestBody`,但是可以有多个`@RequestParam``@PathVariable`**。 如果你的方法必须要用两个 `@RequestBody`来接受数据的话,大概率是你的数据库设计或者系统设计出问题了!

View File

@ -60,7 +60,7 @@ public class OrdersService {
## 2. 事务的特性ACID了解么?
![](images/spring-transaction/bda7231b-ab05-4e23-95ee-89ac90ac7fcf.png)
![](./images/spring-transaction/bda7231b-ab05-4e23-95ee-89ac90ac7fcf.png)
- **原子性Atomicity** 一个事务transaction中的所有操作或者全部完成或者全部不完成不会结束在中间某个环节。事务在执行过程中发生错误会被回滚Rollback到事务开始前的状态就像这个事务从来没有执行过一样。即事务不可分割、不可约简。
- **一致性Consistency** 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
@ -162,7 +162,7 @@ Spring 框架中,事务管理相关最重要的 3 个接口如下:
**`PlatformTransactionManager` 接口的具体实现如下:**
![](images/spring-transaction/ae964c2c-7289-441c-bddd-511161f51ee1.png)
![](./images/spring-transaction/ae964c2c-7289-441c-bddd-511161f51ee1.png)
`PlatformTransactionManager`接口中定义了三个方法:
@ -186,7 +186,7 @@ public interface PlatformTransactionManager {
主要是因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。我前段时间分享过:**“为什么我们要用接口?”**
![](images/spring-transaction/接口使用原因.png)
![](./images/spring-transaction/接口使用原因.png)
#### 3.2.2. TransactionDefinition:事务属性
@ -198,7 +198,7 @@ public interface PlatformTransactionManager {
事务属性包含了 5 个方面:
![](images/spring-transaction/a616b84d-9eea-4ad1-b4fc-461ff05e951d.png)
![](./images/spring-transaction/a616b84d-9eea-4ad1-b4fc-461ff05e951d.png)
`TransactionDefinition` 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。
@ -556,7 +556,7 @@ public interface TransactionDefinition {
这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下事务只有遇到运行期异常RuntimeException 的子类时才会回滚Error 也会导致事务回滚但是在遇到检查型Checked异常时不会回滚。
![](images/spring-transaction/f6c6f0aa-0f26-49e1-84b3-7f838c7379d1.png)
![](./images/spring-transaction/f6c6f0aa-0f26-49e1-84b3-7f838c7379d1.png)
如果你想要回滚你定义的特定的异常类型的话,可以这样:

View File

@ -66,10 +66,7 @@ Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理
具体原理如下图所示,理解起来其实很简单,每当我们提交更新一个文件之后,系统都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。
<div align="center">
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3deltas.png" width="500px"/>
</br>
</div>
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3deltas.png)
**我们怎样才能得到一个文件的最终版本呢?**
@ -81,11 +78,7 @@ Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理
Git 不按照以上方式对待或保存数据。 反之Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效如果文件没有修改Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 **快照流**
<div align="center">
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3snapshots.png" width="500px"/>
</br>
</div>
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3snapshots.png)
### Git 的三种状态
@ -97,9 +90,8 @@ Git 有三种状态,你的文件可能处于其中之一:
由此引入 Git 项目的三个工作区域的概念:**Git 仓库(.git directory)**、**工作目录(Working Directory)** 以及 **暂存区域(Staging Area)**
<div align="center">
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3areas.png" width="500px"/>
</div>
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3areas.png)
**基本的 Git 工作流程如下:**
@ -207,9 +199,8 @@ git branch test
git checkout test
```
<div align="center">
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3切换分支.png" width="500px"/>
</div>
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3切换分支.png)
你也可以直接这样创建分支并切换过去(上面两条命令的合写)

View File

@ -1,18 +1,27 @@
{
"name": "javaguide-blog",
"version": "1.0.0",
"description": "A project of vuepress-theme-hope",
"name": "javaguide",
"version": "2.0.0-alpha.40",
"private": true,
"description": "javaguide",
"license": "MIT",
"author": "Mr.Hope",
"scripts": {
"build": "vuepress build docs",
"clean-dev": "vuepress dev docs --no-cache",
"dev": "vuepress dev docs",
"eject-theme": "vuepress eject-hope docs"
"vite-build": "vuepress-vite build docs",
"vite-clean-serve": "vuepress-vite dev docs --clean-cache",
"vite-serve": "vuepress-vite dev docs",
"webpack-build": "vuepress-webpack build docs",
"webpack-clean-serve": "vuepress-webpack dev docs --clean-cache",
"webpack-serve": "vuepress-webpack dev docs"
},
"devDependencies": {
"compression-webpack-plugin": "^5.0.0",
"vuepress": "^1.8.2",
"vuepress-theme-hope": "^1.20.5"
},
"dependencies": {}
"@vuepress/plugin-docsearch": "2.0.0-beta.36",
"@vuepress/plugin-pwa": "2.0.0-beta.36",
"@vuepress/plugin-pwa-popup": "2.0.0-beta.36",
"@vuepress/plugin-register-components": "2.0.0-beta.36",
"@vuepress/plugin-shiki": "2.0.0-beta.36",
"cross-env": "7.0.3",
"vuepress-theme-hope": "2.0.0-alpha.40",
"vuepress-vite": "2.0.0-beta.36",
"vuepress-webpack": "2.0.0-beta.36"
}
}