mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
Compare commits
10 Commits
4187aebf8b
...
65a56ab31a
Author | SHA1 | Date | |
---|---|---|---|
|
65a56ab31a | ||
|
24cf144def | ||
|
5580b863ae | ||
|
12e14c5868 | ||
|
e48eb637ac | ||
|
17ed881ebe | ||
|
db40b886a5 | ||
|
bb824b6d9c | ||
|
f5020958d6 | ||
|
661226ab4e |
4
.gitignore
vendored
4
.gitignore
vendored
@ -10,9 +10,9 @@ node_modules/
|
||||
**/.vuepress/.temp/
|
||||
# VuePress Output
|
||||
dist/
|
||||
# Build files
|
||||
packages/*/lib/
|
||||
traversal-folder-replace-string.py
|
||||
format-markdown.py
|
||||
|
||||
.npmrc
|
||||
package-lock.json
|
||||
lintmd-config.json
|
||||
|
@ -11,8 +11,6 @@ export default hopeTheme({
|
||||
logo: "/logo.png",
|
||||
favicon: "/favicon.ico",
|
||||
|
||||
iconAssets: "//at.alicdn.com/t/c/font_2922463_o9q9dxmps9.css",
|
||||
|
||||
author: {
|
||||
name: "Guide",
|
||||
url: "https://javaguide.cn/article/",
|
||||
@ -78,6 +76,10 @@ export default hopeTheme({
|
||||
rss: true,
|
||||
},
|
||||
|
||||
icon: {
|
||||
assets: "//at.alicdn.com/t/c/font_2922463_o9q9dxmps9.css",
|
||||
},
|
||||
|
||||
search: {
|
||||
isSearchable: (page) => page.path !== "/",
|
||||
maxSuggestions: 10,
|
||||
|
@ -1,70 +0,0 @@
|
||||
---
|
||||
title: 个人介绍 Q&A
|
||||
category: 走近作者
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
这篇文章我会通过 Q&A 的形式简单介绍一下我自己。
|
||||
|
||||
## 我是什么时候毕业的?
|
||||
|
||||
很多老读者应该比较清楚,我是 19 年本科毕业的,刚毕业就去了某家外企“养老”。
|
||||
|
||||
我的学校背景是比较差的,高考失利,勉强过了一本线 20 来分,去了荆州的一所很普通的双非一本。不过,还好我没有因为学校而放弃自己,反倒是比身边的同学都要更努力,整个大学还算过的比较充实。
|
||||
|
||||
下面这张是当时拍的毕业照(后排最中间的就是我):
|
||||
|
||||

|
||||
|
||||
## 我坚持写了多久博客?
|
||||
|
||||
时间真快啊!我自己是从大二开始写博客的。那时候就是随意地在博客平台上发发自己的学习笔记和自己写的程序。就比如 [谢希仁老师的《计算机网络》内容总结](../cs-basics/network/computer-network-xiexiren-summary.md) 这篇文章就是我在大二学习计算机网络这门课的时候对照着教材总结的。
|
||||
|
||||
身边也有很多小伙伴经常问我:“我现在写博客还晚么?”
|
||||
|
||||
我觉得哈!如果你想做什么事情,尽量少问迟不迟,多问自己值不值得,只要你觉得有意义,就尽快开始做吧!人生很奇妙,我们每一步的重大决定,都会对自己未来的人生轨迹产生影响。是好还是坏,也只有我们自己知道了!
|
||||
|
||||
对我自己来说,坚持写博客这一项决定对我人生轨迹产生的影响是非常正面的!所以,我也推荐大家养成坚持写博客的习惯。
|
||||
|
||||
## 我在大学期间赚了多少钱?
|
||||
|
||||
在校期间,我还通过办培训班、接私活、技术培训、编程竞赛等方式变现 20w+,成功实现“经济独立”。我用自己赚的钱去了重庆、三亚、恩施、青岛等地旅游,还给家里补贴了很多,减轻了父母的负担。
|
||||
|
||||
下面这张是我大一下学期办补习班的时候拍的(离开前的最后一顿饭):
|
||||
|
||||

|
||||
|
||||
下面这张是我大三去三亚的时候拍的:
|
||||
|
||||

|
||||
|
||||
其实,我在大学就这么努力地开始赚钱,也主要是因为家庭条件太一般,父母赚钱都太辛苦了!也正是因为我自己迫切地想要减轻父母的负担,所以才会去尝试这么多赚钱的方法。
|
||||
|
||||
我发现做咱们程序员这行的,很多人的家庭条件都挺一般的,选择这个行业的很大原因不是因为自己喜欢,而是为了多赚点钱。
|
||||
|
||||
如果你也想通过接私活变现的话,可以在我的公众号后台回复“**接私活**”来了解一些我的个人经验分享。
|
||||
|
||||
::: center
|
||||
|
||||

|
||||
|
||||
:::
|
||||
|
||||
## 为什么自称 Guide?
|
||||
|
||||
可能是因为我的项目名字叫做 JavaGuide , 所以导致有很多人称呼我为 **Guide 哥**。
|
||||
|
||||
后面,为了读者更方便称呼,我就将自己的笔名改成了 **Guide**。
|
||||
|
||||
我早期写文章用的笔名是 SnailClimb 。很多人不知道这个名字是啥意思,给大家拆解一下就清楚了。SnailClimb=Snail(蜗牛)+Climb(攀登)。我从小就非常喜欢听周杰伦的歌曲,特别是他的《蜗牛》🐌 这首歌曲,另外,当年我高考发挥的算是比较失常,上了大学之后还算是比较“奋青”,所以,我就给自己起的笔名叫做 SnailClimb ,寓意自己要不断向上攀登,嘿嘿 😁
|
||||
|
||||

|
||||
|
||||
## 后记
|
||||
|
||||
凡心所向,素履所往,生如逆旅,一苇以航。
|
||||
|
||||
生活本就是有苦有甜。共勉!
|
||||
|
||||

|
@ -727,7 +727,7 @@ Bloom Filter 会使用一个较大的 bit 数组来保存所有的数据,数
|
||||
|
||||
#### 缓存雪崩和缓存击穿有什么区别?
|
||||
|
||||
缓存雪崩和缓存击穿比较像,但缓存雪崩导致的原因是缓存中的大量或者所有数据失效,缓存击穿导致的原因主要是某个热点数据不存在与缓存中(通常是因为缓存中的那份数据已经过期)。
|
||||
缓存雪崩和缓存击穿比较像,但缓存雪崩导致的原因是缓存中的大量或者所有数据失效,缓存击穿导致的原因主要是某个热点数据不存在于缓存中(通常是因为缓存中的那份数据已经过期)。
|
||||
|
||||
### 如何保证缓存和数据库数据的一致性?
|
||||
|
||||
|
@ -110,14 +110,30 @@ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
|
||||
|
||||
通过上面的内容我们已经知道:每个线程维护一个名为 `ThreadLocalMap` 的 map。 当你使用 `ThreadLocal` 存储值时,实际上是将值存储在当前线程的 `ThreadLocalMap` 中,其中 `ThreadLocal` 实例本身作为 key,而你要存储的值作为 value。
|
||||
|
||||
`ThreadLocalMap` 的 `key` 和 `value` 引用机制:
|
||||
`ThreadLocal` 的 `set()` 方法源码如下:
|
||||
|
||||
- **key 是弱引用**:`ThreadLocalMap` 中的 key 是 `ThreadLocal` 的弱引用 (`WeakReference<ThreadLocal<?>>`)。 这意味着,如果 `ThreadLocal` 实例不再被任何强引用指向,垃圾回收器会在下次 GC 时回收该实例,导致 `ThreadLocalMap` 中对应的 key 变为 `null`。
|
||||
- **value 是强引用**:`ThreadLocalMap` 中的 value 是强引用。 即使 key 被回收(变为 `null`),value 仍然存在于 `ThreadLocalMap` 中,被强引用,不会被回收。
|
||||
```java
|
||||
public void set(T value) {
|
||||
Thread t = Thread.currentThread(); // 获取当前线程
|
||||
ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap
|
||||
if (map != null) {
|
||||
map.set(this, value); // 设置值
|
||||
} else {
|
||||
createMap(t, value); // 创建新的 ThreadLocalMap
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ThreadLocalMap` 的 `set()` 和 `createMap()` 方法中,并没有直接存储 `ThreadLocal` 对象本身,而是使用 `ThreadLocal` 的哈希值计算数组索引,最终存储于类型为`static class Entry extends WeakReference<ThreadLocal<?>>`的数组中。
|
||||
|
||||
```java
|
||||
int i = key.threadLocalHashCode & (len-1);
|
||||
```
|
||||
|
||||
`ThreadLocalMap` 的 `Entry` 定义如下:
|
||||
|
||||
```java
|
||||
static class Entry extends WeakReference<ThreadLocal<?>> {
|
||||
/** The value associated with this ThreadLocal. */
|
||||
Object value;
|
||||
|
||||
Entry(ThreadLocal<?> k, Object v) {
|
||||
@ -127,6 +143,11 @@ static class Entry extends WeakReference<ThreadLocal<?>> {
|
||||
}
|
||||
```
|
||||
|
||||
`ThreadLocalMap` 的 `key` 和 `value` 引用机制:
|
||||
|
||||
- **key 是弱引用**:`ThreadLocalMap` 中的 key 是 `ThreadLocal` 的弱引用 (`WeakReference<ThreadLocal<?>>`)。 这意味着,如果 `ThreadLocal` 实例不再被任何强引用指向,垃圾回收器会在下次 GC 时回收该实例,导致 `ThreadLocalMap` 中对应的 key 变为 `null`。
|
||||
- **value 是强引用**:即使 `key` 被 GC 回收,`value` 仍然被 `ThreadLocalMap.Entry` 强引用存在,无法被 GC 回收。
|
||||
|
||||
当 `ThreadLocal` 实例失去强引用后,其对应的 value 仍然存在于 `ThreadLocalMap` 中,因为 `Entry` 对象强引用了它。如果线程持续存活(例如线程池中的线程),`ThreadLocalMap` 也会一直存在,导致 key 为 `null` 的 entry 无法被垃圾回收,机会造成内存泄漏。
|
||||
|
||||
也就是说,内存泄漏的发生需要同时满足两个条件:
|
||||
|
@ -109,16 +109,14 @@ tag:
|
||||
|
||||
对于初始化阶段,虚拟机严格规范了有且只有 6 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):
|
||||
|
||||
1. 当遇到 `new`、 `getstatic`、`putstatic` 或 `invokestatic` 这 4 条字节码指令时,比如 `new` 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
|
||||
- 当 jvm 执行 `new` 指令时会初始化类。即当程序创建一个类的实例对象。
|
||||
- 当 jvm 执行 `getstatic` 指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
|
||||
- 当 jvm 执行 `putstatic` 指令时会初始化类。即程序给类的静态变量赋值。
|
||||
- 当 jvm 执行 `invokestatic` 指令时会初始化类。即程序调用类的静态方法。
|
||||
1. 遇到 `new`、`getstatic`、`putstatic` 或 `invokestatic` 这 4 条字节码指令时:
|
||||
- `new`: 创建一个类的实例对象。
|
||||
- `getstatic`、`putstatic`: 读取或设置一个类型的静态字段(被 `final` 修饰、已在编译期把结果放入常量池的静态字段除外)。
|
||||
- `invokestatic`: 调用类的静态方法。
|
||||
2. 使用 `java.lang.reflect` 包的方法对类进行反射调用时如 `Class.forName("...")`, `newInstance()` 等等。如果类没初始化,需要触发其初始化。
|
||||
3. 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
|
||||
4. 当虚拟机启动时,用户需要定义一个要执行的主类 (包含 `main` 方法的那个类),虚拟机会先初始化这个类。
|
||||
5. `MethodHandle` 和 `VarHandle` 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用,
|
||||
就必须先使用 `findStaticVarHandle` 来初始化要调用的类。
|
||||
5. `MethodHandle` 和 `VarHandle` 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用,就必须先使用 `findStaticVarHandle` 来初始化要调用的类。
|
||||
6. **「补充,来自[issue745](https://github.com/Snailclimb/JavaGuide/issues/745 "issue745")」** 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
|
||||
|
||||
## 类卸载
|
||||
|
@ -883,7 +883,7 @@ class B {
|
||||
- 那么此时就去三级缓存中调用 `getObject()` 方法去获取 A 的 **前期暴露的对象** ,也就是调用上边加入的 `getEarlyBeanReference()` 方法,生成一个 A 的 **前期暴露对象**;
|
||||
- 然后就将这个 `ObjectFactory` 从三级缓存中移除,并且将前期暴露对象放入到二级缓存中,那么 B 就将这个前期暴露对象注入到依赖,来支持循环依赖。
|
||||
|
||||
**只用两级缓存够吗?** 在没有 AOP 的情况下,确实可以只使用一级和三级缓存来解决循环依赖问题。但是,当涉及到 AOP 时,二级缓存就显得非常重要了,因为它确保了即使在 Bean 的创建过程中有多次对早期引用的请求,也始终只返回同一个代理对象,从而避免了同一个 Bean 有多个代理对象的问题。
|
||||
**只用两级缓存够吗?** 在没有 AOP 的情况下,确实可以只使用一级和二级缓存来解决循环依赖问题。但是,当涉及到 AOP 时,三级缓存就显得非常重要了,因为它确保了即使在 Bean 的创建过程中有多次对早期引用的请求,也始终只返回同一个代理对象,从而避免了同一个 Bean 有多个代理对象的问题。
|
||||
|
||||
**最后总结一下 Spring 如何解决三级缓存**:
|
||||
|
||||
|
12
package.json
12
package.json
@ -21,17 +21,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.19",
|
||||
"@vuepress/plugin-feed": "2.0.0-rc.66",
|
||||
"@vuepress/plugin-search": "2.0.0-rc.66",
|
||||
"@vuepress/plugin-feed": "2.0.0-rc.70",
|
||||
"@vuepress/plugin-search": "2.0.0-rc.70",
|
||||
"husky": "9.1.7",
|
||||
"markdownlint-cli2": "0.16.0",
|
||||
"markdownlint-cli2": "0.17.1",
|
||||
"mathjax-full": "3.2.2",
|
||||
"nano-staged": "0.8.0",
|
||||
"prettier": "3.4.2",
|
||||
"sass-embedded": "1.83.0",
|
||||
"sass-embedded": "1.83.1",
|
||||
"vue": "^3.5.13",
|
||||
"vuepress": "2.0.0-rc.19",
|
||||
"vuepress-theme-hope": "2.0.0-rc.64"
|
||||
"vuepress-theme-hope": "2.0.0-rc.68"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0"
|
||||
"packageManager": "pnpm@10.0.0"
|
||||
}
|
||||
|
6866
pnpm-lock.yaml
generated
6866
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user