Skip to content

Commit

Permalink
add:spring
Browse files Browse the repository at this point in the history
  • Loading branch information
lbwanga committed Sep 25, 2024
1 parent 9fe2195 commit 6a50c90
Show file tree
Hide file tree
Showing 39 changed files with 2,168 additions and 390 deletions.
30 changes: 18 additions & 12 deletions docs/JUC/JUC.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ ObjectMonitor() {

### 锁膨胀

锁一共有 4 种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

锁一共有 4 种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁,目的是为了提高获得锁和释放锁的效率。

当一个线程访问同步块时设置为偏向锁,偏向锁存在锁竞争时升级为轻量级锁,轻量级锁自旋失败后升级为重量级锁。

#### 偏向锁

Expand Down Expand Up @@ -297,13 +297,11 @@ Java内存模型规定了所有的变量都存储在主内存中。每条线程

`volatile、synchronized和final`关键字保证可见性。



**有序性**

如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程, 所有的操作都是无序的,指“指令重排序”现象和“工作内存与主内存同步延迟”现象。

Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性
在Java中使用了happens-before原则来确保有序性



Expand All @@ -312,7 +310,7 @@ Java语言提供了volatile和synchronized两个关键字来保证线程之间
当一个变量被定义成volatile之后,它将具备两项特性:

1. 保证此变量对所有线程的可见性。而普通变量并不能做到这一点,普通变量的值在线程间传递时均需要通过主内存来完成。当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存;当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效,从主内存中读取共享变量。
2. 禁止指令重排序优化普通的变量仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。
2. 禁止指令重排序优化普通的变量仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。



Expand Down Expand Up @@ -356,6 +354,10 @@ volatile重排序规则:



volatile关键字在Java中主要通过内存屏障来禁止特定类型的指令重排序。



synchronized 无法禁止指令重排和处理器优化,为什么可以保证有序性可见性?

1. 加了锁之后,只能有一个线程获得到了锁,获得不到锁的线程就要阻塞,所以同一时间只有一个线程执行,相当于单线程,由于数据依赖性的存在,单线程的指令重排是没有问题的
Expand Down Expand Up @@ -465,7 +467,7 @@ public class Singleton {
| Runnable(运行) | Java线程将操作系统的就绪和运行状态合并。调用了 t.start() 方法 |
| Blocked(阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态 |
| Waiting(等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态,进入这个状态后不能自动唤醒,必须等待另一个线程调用 notify 或者 notifyAll 方法才能唤醒。在这种状态下,线程将不会消耗CPU资源 |
| Timed Waiting (限期等待) | 有几个方法有超时参数,调用将进入 Timed Waiting 状态,这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait |
| Timed Waiting (限期等待) | 有几个方法有超时参数,调用将进入 Timed Waiting 状态,这一状态将一直保持到超时期满或者接收到唤醒通知。 |
| Teminated(终止) | run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡 |

![](./JUC/线程状态.jpeg)
Expand Down Expand Up @@ -626,7 +628,7 @@ Thread 类 API:
| join() | 等待线程结束。调用某个线程的 `join()` 方法,当前线程会等待该线程执行完成。 | |
| join(long millis) | 等待这个线程结束,最多 millis 毫秒,0 意味着永远等待 | |
| wait() | 当前线程进入等待状态,直到被 `notify()``notifyAll()` 唤醒。必须在同步块或同步方法中调用。 | |
| notify() | 醒一个正在等待该对象监视器的线程。被唤醒的线程会进入 Runnable 状态,但不会立即获得锁。 | |
| notify() | 醒一个正在等待该对象监视器的线程。被唤醒的线程会进入 Runnable 状态,但不会立即获得锁。 | hotspot对notofy()的实现并不是我们以为的随机唤醒,,而是“先进先出”的顺序唤醒 |
| notifyAll() | 唤醒所有正在等待该对象监视器的线程。 | |


Expand Down Expand Up @@ -1182,7 +1184,7 @@ ReentrantLock 相对于 synchronized 具备如下特点:
1. 锁的实现:synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的
2. 性能:新版本 Java 对 synchronized 进行了很多优化,synchronized 与 ReentrantLock 大致相同
3. 使用:ReentrantLock 需要手动解锁,synchronized 执行完代码块自动解锁
4. **可中断**:ReentrantLock 可中断,而 synchronized 不行。这里的可中断是指在获取锁的过程中,可以被取消获取锁的请求
4. **可中断**:ReentrantLock 可中断,而 synchronized 不行。这里的可中断是指线程在等待锁的过程中,可以被其他线程中断而提前结束等待
5. **公平锁**:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。ReentrantLock 可以设置公平锁或不公平锁,synchronized 中的锁是非公平的
6. **锁超时**:在指定的截止时间之前获取锁,如果截止时间到了仍无法获取锁,则返回。
7. 锁绑定多个条件:一个 ReentrantLock 可以同时绑定多个 Condition 对象,更细粒度的唤醒线程
Expand Down Expand Up @@ -2622,14 +2624,14 @@ Future 接口有 5 个方法,它们分别是取消任务的方法 `cancel()`

### 关闭方法

可以通过调用线程池的 shutdown 或 shutdownNow 方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,所以无法响应中断的任务可能永远无法终止
可以通过调用线程池的 shutdown 或 shutdownNow 方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,它只能设置线程的中断标志。线程需要在执行中显式地检查这个标志,或者在阻塞操作(如 `sleep()``wait()``join()` 等)时捕获 `InterruptedException`,才能真正响应中断信号

ExecutorService 类 API:

| 方法 | 说明 |
| ---------------------- | ------------------------------------------------------------ |
| void shutdown() | 将线程池的状态设置成 SHUTDOWN,然后中断所有没有正在执行任务的线程|
| List shutdownNow() | 将线程池的状态设置成 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表 |
| void shutdown() | 将线程池的状态设置成 SHUTDOWN,正在执行的任务会继续执行下去,没有被执行的则中断,不能再往线程池中添加任何任务|
| List shutdownNow() | 将线程池的状态设置成 STOP,试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,返回那些未执行的任务。 |
| boolean isShutdown() | 调用 `shutdown()``shutdownNow()` 方法后返回为 true。 |
| boolean isTerminated() | 当所有的任务都已关闭后,才表示线程池关闭成功,返回为 true |

Expand Down Expand Up @@ -3396,3 +3398,7 @@ private void resize() {
`rehash()`首先是会进行探测式清理工作,从`table`的起始位置往后清理。清理完成之后,`table`中可能有一些`key`为`null`的`Entry`数据被清理掉,所以此时通过判断`size >= threshold - threshold / 4` 也就是`size >= threshold * 3/4` 来决定是否扩容。

扩容后的`tab`的大小为`oldLen * 2`,然后遍历老的散列表,重新计算`hash`位置,然后放到新的`tab`数组中,如果出现`hash`冲突则往后寻找最近的`entry`为`null`的槽位,遍历完成之后,`oldTab`中所有的`entry`数据都已经放入到新的`tab`中了。



## CompletableFuture
54 changes: 32 additions & 22 deletions docs/JVM/JVM.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,9 @@ protected synchronized Class<?> loadClass(String name, boolean resolve) throws C



## 运行时数据区
## 运行时数据区

![](./JVM/运行时数据区.png)
![](./JVM/JVM (2).png)

### 程序计数器

Expand Down Expand Up @@ -509,21 +509,15 @@ Java服务端程序开发时,**建议将-Xmx和-Xms设置为相同的值**,

### 方法区

方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区是《Java虚拟机规范》中设计的**虚拟概念**,是一块逻辑区域,主要包含三部分内容:

- 类的元信息,保存了所有类的基本信息
- 运行时常量池,保存了字节码文件中的常量池内容
- 字符串常量池,保存了字符串常量
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的**类信息、常量、静态变量、即时编译器编译后的代码缓存**等数据。方法区是《Java虚拟机规范》中设计的**虚拟概念**,是一块逻辑区域。



方法区是《Java虚拟机规范》中设计的虚拟概念,每款Java虚拟机在实现上都各不相同。Hotspot设计如下:

* **JDK8**之前的版本方法区由**堆区域中的永久代空间(ps_perm_gen)**实现,堆的大小由虚拟机参数来控制。

* **JDK8**及之后的版本方法区存由**元空间(metaspace)**实现,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受的上限,可以一直分配。可以使用 `-XX:MaxMetaspaceSize=值` 将元空间最大大小进行限制。
* **JDK8**及之后的版本方法区由**元空间(metaspace)**实现,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受的上限,可以一直分配。可以使用 `-XX:MaxMetaspaceSize=值` 将元空间最大大小进行限制。



Expand Down Expand Up @@ -775,16 +769,16 @@ HotSpot 虚拟机主要使用直接指针来进行对象访问。

Java使用的是可达性分析算法来判断对象是否可以被回收。

基本思路就是通过 一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的,可以被回收

固定可作为GC Roots的对象包括:

* 在虚拟机栈(栈帧中的本地变量表)中引用的对象。
* 本地方法栈中引用的对象。
* 在方法区中类静态属性引用的对象。
* 在方法区中常量引用的对象,譬如字符串常量池里的引用。
* Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
* 所有被同步锁(synchronized关键字)持有的对象(即该对象作为锁被使用)。
* Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
* 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。


Expand All @@ -809,9 +803,11 @@ Java里传统引用的定义: 如果reference类型的数据中存储的数值

弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。

弱引用的使用场景:缓存系统、对象池、避免内存泄漏

**虚引用:**

虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的 存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。



Expand Down Expand Up @@ -901,11 +897,13 @@ Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用

#### 分代收集算法

分代垃圾回收将整个内存区域划分为年轻代和老年代
分代收集算法将整个内存区域划分为年轻代和老年代

- 新生代:复制算法
- 老年代:标记-清除算法、标记-整理算法

对象创建时,一般在新生代申请内存,当经历一次 GC 之后如果对还存活,那么对象的年龄 +1。当年龄超过一定值后,如果对象还存活,那么该对象会进入老年代。

![](./JVM/分代垃圾回收.png)

我们通过arthas来验证下内存划分的情况:
Expand Down Expand Up @@ -946,7 +944,7 @@ tenured_gen指的是晋升区域,其实就是老年代。

部分收集 (Partial GC):

- 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;
- 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集,包括Eden区和两个Survivor区(S0和S1)
- 老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代整堆收集;
- 混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。

Expand All @@ -956,13 +954,14 @@ tenured_gen指的是晋升区域,其实就是老年代。

**Minor GC触发条件**:Eden区满时

**Major GC触发条件**:当老年代空间不足时,或者系统检测到年轻代对象晋升到老年代的速度过快,可能会触发Major GC。

**Full GC触发条件**

1. 调用System.gc时,系统建议执行Full GC,但是不必然执行。可以通过 `-XX:+ DisableExplicitGC` 来禁止调用 `System.gc()`
1. 调用 `System.gc`,系统建议执行Full GC,但是不必然执行。可以通过 `-XX:+ DisableExplicitGC` 来禁止调用 `System.gc()`
2. 老年代空间不足。
3. 永久代空间不足。JVM 规范中运行时数据区域中的方法区,在 HotSpot 虚拟机中也称为永久代,存放一些类信息、常量、静态变量等数据,当系统要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,会触发 Full GC。
4. 通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
5. 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。
3. 当永久代(Java 8之前的版本)或元空间(Java 8及以后的版本)空间不足时。JVM 规范中运行时数据区域中的方法区,在 HotSpot 虚拟机中也称为永久代,存放一些类信息、常量、静态变量等数据,当系统要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,会触发 Full GC。
4. Minor GC(新生代垃圾回收)时,如果存活的对象无法全部放入老年代,或者老年代空间不足以容纳存活的对象,则会触发Full GC,对整个堆内存进行回收。



Expand All @@ -989,13 +988,13 @@ tenured_gen指的是晋升区域,其实就是老年代。

### 垃圾回收器

垃圾回收器是垃圾回收算法的具体实现。
垃圾回收器是垃圾回收算法的具体实现。

由于垃圾回收器分为年轻代和老年代,除了G1之外其他垃圾回收器必须成对组合进行使用。

![](./JVM/垃圾回收器.png)

![](./JVM/新一代GC.png)
![](./JVM/垃圾收集器.png)

#### Serial/Serial Old

Expand Down Expand Up @@ -1104,13 +1103,24 @@ G1收集器的运作过程大致可划分为四个步骤:

![](./JVM/G1.jpeg)



| 垃圾回收器 | 使用范围 | STW | 垃圾回收算法 | 回收过程 | 浮动垃圾 | 使用场景 |
| ---------- | -------------- | ------------------------ | --------------- | ----------------------------------------------- | ------------ | ---------------------------- |
| CMS | 老年代 | 以最小的停顿时间为目标 | 标记-清除 | 1. 初始标记 2. 并发标记 3. 重新标记 4. 并发清除 | 产生浮动垃圾 | 低延迟需求;老年代收集; |
| G1 | 新生代和老年代 | 极高概率满足停顿时间要求 | 复制、标记-整理 | 1. 初始标记 2. 并发标记 3. 最终标记 4. 筛选回收 | 没有浮动垃圾 | 大堆内存;低停顿时间高吞吐量 |



#### Shenandoah GC



#### ZGC



## 原理篇
## 原理

### 栈上的数据存储

Expand Down
Binary file added docs/JVM/JVM/JVM (2).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/JVM/JVM/垃圾收集器.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6a50c90

Please sign in to comment.