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

|
||||

|
||||
|
||||
**JDK7/8**
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0016.png">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0016.png">
|
||||
|
||||
|
||||
|
||||
@@ -1080,7 +1080,7 @@ public class StringExer1 {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0017.png">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0017.png">
|
||||
|
||||
**练习3**
|
||||
|
||||
@@ -1169,11 +1169,11 @@ public class StringIntern2 {
|
||||
arr[i] = new String(String.valueOf(data[i % data.length]));
|
||||
```
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0018.png">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0018.png">
|
||||
|
||||
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0019.png">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0019.png">
|
||||
|
||||
2、使用 intern() 方法:由于数组中字符串的引用都指向字符串常量池中的字符串,所以程序需要维护的 String 对象更少,内存占用也更低
|
||||
|
||||
@@ -1182,11 +1182,11 @@ arr[i] = new String(String.valueOf(data[i % data.length]));
|
||||
arr[i] = new String(String.valueOf(data[i % data.length])).intern();
|
||||
```
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0020.png" >
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0020.png" >
|
||||
|
||||
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0021.png">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0021.png">
|
||||
|
||||
|
||||
|
||||
@@ -1218,11 +1218,11 @@ public class StringGCTest {
|
||||
* Number of entries 和 Number of literals 明显没有 100000
|
||||
* 以上两点均说明 StringTable 区发生了垃圾回收
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0022.jpg">
|
||||
|
||||
|
||||
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>
|
||||
<img src="https://img.imlql.cn/youthlql@1.0.8/JVM/chapter_009/0023.jpg"/>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user