jvm_book/README.md
2021-02-10 10:24:42 +08:00

309 lines
24 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 《深入理解Java虚拟机第3版
> **广告**<br/>
> 我正在撰写的《[现代软件架构探索:The Fenix Project](https://icyfenix.cn)》:[https://github.com/fenixsoft/awesome-fenix](https://github.com/fenixsoft/awesome-fenix)
>
> 这是一部以“讨论如何构筑一套可靠的分布式大型软件系统”为主题的、完全免费的开源文档如对您有用望不吝给个Star
<br/>
  本工程为《深入理解Java虚拟机第3版》书中的样例代码以方便读者自行测试。部分代码需要在特定的虚拟机版本、参数下运行在代码前均已“VM Args”的注释进行标注。
  书中的勘误也会在本文中持续更新读者可通过issues提交新的勘误如对新版有任何建议也欢迎以[issues](https://github.com/fenixsoft/jvm_book/issues)或任何其他您方便的形式提出。
### 勘误列表:
- **目录XIVPage164** 4.3.3 VisualVM多合 - 故障处理工具中”的【多合 - 】应该是【多合一】
<br/>这个在原稿上是多合一,但印刷后确实怎么看都像是“多合 - ”,请编辑看看。
- **Page 202**如果使用【客户端】模式的虚拟机启动Eclipse将会使用到C2编译器
<br>更正如果使用【服务端】模式的虚拟机启动Eclipse将会使用到C2编译器
- **Page 378**我们就只有使用【JSR-296】中定义的插入式注解处理器API来对Java编译子系统的行为施加影响
<br>更正我们就只有使用【JSR-269】中定义的插入式注解处理器API来对Java编译子系统的行为施加影响
#### 以下勘误内容已在第7次重印版2021-1-11日修正
------
- **#7-1 Page 99**作为CMS收集器的替代者和继承人设计者们希望做出一款能够建立起“【停顿时间模型】”Pause Prediction Model的收集器【停顿时间模型】的意思是能够支持指定在一个长度为M毫秒的时间片段内
<br>全书术语统一为作为CMS收集器的替代者和继承人设计者们希望做出一款能够建立起“【停顿预测模型】”Pause Prediction Model的收集器【停顿预测模型】的意思是能够支持指定在一个长度为M毫秒的时间片段内
- **#7-1 Page 85 & 87**:两处代码部分:<br>
`CARD_TABLE [this address >> 9] = 0;`和`if (CARD_TABLE [this address >> 9] != 0) CARD_TABLE [this address >> 9] = 0;`
<br>由于书中文字是以“1标志变脏0标志未变脏”来描述的代码中应该统一起来因此修改为
<br>`CARD_TABLE [this address >> 9] = 1;`和`if (CARD_TABLE [this address >> 9] != 1) CARD_TABLE [this address >> 9] = 1;`
- **#7-2 Page 108**我们再来聊一下Shenandoah用以支持【并行整理】的核心概念
<br>更正我们再来聊一下Shenandoah用以支持【并发整理】的核心概念
- **#7-3 Page 128**表3-4最后一行“ParallelGCThreads”的重复了请编辑删除掉此行另外这个表格中有两个参数是以“=n”结尾的为了格式统一请将“=n”删除掉仅保留参数名称即可。
- **#7-4 Page 182**:服务器的硬件为四路【志强】处理器
<br>更正:服务器的硬件为四路【至强】处理器
- **#7-5 Page 230**脚注Java代码的方法特征签名只包括了方法名称、【参数顺序及参数类型】
<br>更正Java代码的方法特征签名只包括了方法名称、【参数数量、参数顺序及参数类型】
- **#7-6 Page 268**加载阶段既可以使用Java虚拟机里内置的【引导类加载器】来完成
<br>全书术语统一为加载阶段既可以使用Java虚拟机里内置的【启动类加载器】来完成
- **#7-7 Page 268**Java虚拟机将会把数组C标记为与【引导类加载器】关联。
<br>全书术语统一为Java虚拟机将会把数组C标记为与【启动类加载器】关联。
- **#7-8 Page 282**:如果需要把加载请求委派给【引导类加载器】去处理
<br>全书术语统一为:如果需要把加载请求委派给【启动类加载器】去处理
- **#7-9 Page 282**其中的注释和代码实现都明确地说明了以null值来代表【引导类加载器】的约定规则
<br>全书术语统一为其中的注释和代码实现都明确地说明了以null值来代表【启动类加载器】的约定规则
- **#7-10 Page 448**这种操作相当于对缓存中的变量做了一次前面介绍Java内存【模式】中所说的“store和write”操作
<br>更正这种操作相当于对缓存中的变量做了一次前面介绍Java内存【模型】中所说的“store和write”操作
- **#7-11 Page 479**如适应性自旋Adaptive Spinning、锁削除Lock Elimination、【锁膨胀】Lock Coarsening、轻量级锁Lightweight Locking、偏向锁Biased Locking等等
<br>更正如适应性自旋Adaptive Spinning、锁削除Lock Elimination、【锁粗化】Lock Coarsening、轻量级锁Lightweight Locking、偏向锁Biased Locking等等
- **#7-12 Page 483**【同时】使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中
<br>更正【并】使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中
#### 以下勘误内容已在第6次重印版2020-11-5日修正
------
- **#6-1 Page 46**到了JDK 7的HotSpot已经把原本放在永久代的字符串常量池、静态变量等【移出】
<br>更正到了JDK 7的HotSpot已经把原本放在永久代的字符串常量池、静态变量等【移至Java堆中在4.3.1节会通过实验验证这一点】
- **#6-2 Page 70**:在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如【各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等】
<br>有同学反应太过拗口,修改为:在虚拟机栈(栈帧中的本地变量表)中的引用的对象,譬如【当前正在运行的方法所使用到的参数、局部变量、临时变量等】
- **#6-3 Page 94**-XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数也就是垃圾收集时间占总时间的比率这个参数设定为N的话表示用户代码执行时间与总执行时间之比为NN+1。譬如把此参数设置为19那允许的最大垃圾收集时间就占总时间的5%即1 /(1+19)默认值为99就是允许最大1%即1 /(1+99))的垃圾收集时间。
<br>整一段修正为:-XX:GCTimeRatio参数的值应为设置为一个正整数表示用户期望虚拟机消耗在GC上的时间不超过程序运行时间的1/(1+N。默认值为99含义是尽可能保证应用程序执行的时间为收集器执行时间的99倍也即是收集器的时间消耗不超过总运行时间的1%。
- **#6-4 Page 243**表6-26第6行ACC_INTERFACE 【0x0020】
<br>更正ACC_INTERFACE 【0x0200】
- **#6-5 Page 256**如果v在目标类型Tint或long的表示范围之【类】
<br>更正如果v在目标类型Tint或long的表示范围之【内】
- **#6-6 Page 274**:接着由虚拟机生成一个代表该数组维度和元素的【数组对象】。
<br>更正:接着由虚拟机生成一个代表该数组维度和元素的【数组类型】。
- **#6-7 Page 317**本例中为一项【CONSTANT_InterfaceMethodref_info】常量
<br>更正本例中为一项【CONSTANT_Methodref_info】常量
- **#6-8 Page 331**图8-6 执行偏移地址为【1】的指令的情况
<br>更正图8-6 执行偏移地址为【2】的指令的情况
- **#6-9 Page 385**代码清单10-18 第13行需修改方法名称以符合输出结果
<br>`protected void BADLY_NAMED_CODE()` { 修改为 `protected void Test() {`
- **#6-10 Page 392**脚注还有一个不太上台面但其实是Java虚拟机必须支持循环体触发编译的【理由是】诸多跑分软件的测试用例通常都属于第二种如果不去支持跑分会显得成绩很不好看。
<br>更正为还有一个不太上台面但其实是Java虚拟机必须支持循环体触发编译的【理由是】诸多跑分软件的测试用例通常都属于第二种如果不去支持跑分会显得成绩很不好看。
- **#6-11 Page 419**p.y = 42
<br>更正p.y = 42【;】
- **#6-12 Page 420**int py = 42
<br>更正int py = 42【;】
- **#6-13 Page 431**与【图11-11】和【图11-12】所示相比虽然没有了箭头
<br>更正与【图11-13】和【图11-14】所示相比虽然没有了箭头
- **#6-14 Page 443**Java内存模型的基础设计并未改变即使是【这四操作种】对普通用户阅读使用起来仍然是并不方便。
<br>更正Java内存模型的基础设计并未改变即使是【这四种操作】对普通用户阅读使用起来仍然是并不方便。
- **#6-15 Page 467**在【第10章】里我们讲解“final关键字带来的可见性”时曾经提到过这一点
<br>更正在【第12章】里我们讲解“final关键字带来的可见性”时曾经提到过这一点
- **#6-16 Page 472**在【第10章】中我们知道了主流Java虚拟机实现中Java的线程是映射到操作系统的原生内核线程之上的
<br>更正在【第12章】中我们知道了主流Java虚拟机实现中Java的线程是映射到操作系统的原生内核线程之上的
- **#6-17 Page 480**:锁削除是指虚拟机即时编译器在运行时,对一些代码中要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除。
<br>这句话有读者反应不通顺,整句修改为:锁消除是指虚拟机即时编译器在运行时检测到某段需要同步的代码根本不可能存在共享数据竞争而实施的一种对锁进行消除的优化策略。
#### 以下勘误内容已在第5次重印版2020-9-11日修正
------
- **#5-1 Page 51**以下代码清单2-2为HotSpot虚拟机代表Mark Word中的代码【markOop.cpp】注释片段
<br>更正以下代码清单2-2为HotSpot虚拟机代表Mark Word中的代码【markOop.hpp】注释片段。另下面代码清单2-2也应更正为“代码清单2-2 【markOop.hpp】片段”
- **#5-2 Page 72**:要真正宣告一个对象死亡,【至少要】经历两次标记过程
<br>更正:要真正宣告一个对象死亡,【最多会】经历两次标记过程
- **#5-3 Page 78**图3-2、图3-3原稿中第2行2列、3行2列、3行4列都是“可回收”对象的颜色排版重画的时候颜色搞错了麻烦编辑下次印刷中修改过来。
- **#5-4 Page 79**发生垃圾【搜集】时将Eden和Survivor中仍然存活的对象一次性过拷贝到另外一块Survivor空间上
<br>更正发生垃圾【收集】时将Eden和Survivor中仍然存活的对象一次性过拷贝到另外一块Survivor空间上
- **#5-5 Page 94**-XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数也就是垃圾收集时间占总时间的比率【相当于是吞吐量的倒数】。
<br>更正:-XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数也就是垃圾收集时间占总时间的比率【这个参数设定为N的话表示用户代码执行时间与总执行时间之比为NN+1】
- **#5-6 Page 95**Parallel Old是Parallel Scavenge收集器的老年代版本支持多线程【并发收集】
<br>更正Parallel Old是Parallel Scavenge收集器的老年代版本支持多线程【并行收集】
- **#5-7 Page 109**:一旦用户程序访问到归属于旧对象的内存空间就会产生自陷【中段】
<br>更正:一旦用户程序访问到归属于旧对象的内存空间就会产生自陷【中断】
- **#5-8 Page 128**JDK 9之前虚拟机运行在Server模式下的默认值打开此开关后使用【Parallel Scavenge + Serial OldPS MarkSweep的】收集器组合进行内存回收
<br>更正JDK 9之前虚拟机运行在Server模式下的默认值打开此开关后使用【Parallel】收集器组合进行内存回收
- **#5-9 Page 215**Class文件是一组以【8个字节】为基础单位的二进制流各个数据项目严格按照顺序紧凑地排列在文件之中中间没有添加任何分隔符这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据没有空隙存在。当遇到需要占用【8个字节】以上空间的数据项时则会按照高位在前 的方式分割成若干个【8个字节】进行存储。
<br>更正Class文件是一组以【字节】为基础单位的二进制流各个数据项目严格按照顺序紧凑地排列在文件之中中间没有添加任何分隔符这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据没有空隙存在。当遇到需要占用【单个字节】以上空间的数据项时则会按照高位在前 的方式分割成若干个【字节】进行存储。
- **#5-10 Page 259**3 monitorenter // 以【栈定】元素即f作为锁开始同步
<br>更正3 monitorenter // 以【栈顶】元素即f作为锁开始同步
- **#5-11 Page 265**上述代码运行之后只会输出“SuperClass init!”
<br>更正上述代码运行之后【除value的值外】只会输出“SuperClass init!”
- **#5-12 Page 280**成为了Java技术体系中一块重要的基石可谓是【失之桑榆收之东隅】。
<br>更正成为了Java技术体系中一块重要的基石可谓是【失之东隅收之桑榆】。
- **#5-13 Page 307**'a'除了可以代表一个【字符串】
<br>更正:'a'除了可以代表一个【字符】
- **#5-14 Page 392**:脚注:是诸多跑分软件的测试【用力】通常都属于第二种,如果不去支持跑分会显得成绩很不好看。
<br>更正:是诸多跑分软件的测试【用例】通常都属于第二种,如果不去支持跑分会显得成绩很不好看。
- **#5-15 Page 432**在Outline视图中找到创建理想图的方法是【greateGraph()】
<br>更正在Outline视图中找到创建理想图的方法是【createGraph()】这个笔误在432页一共有3处
- **#5-16 Page 472**脚注2由于本书的主题是Java虚拟机【和】不是Java并发编程
<br>更正由于本书的主题是Java虚拟机【而】不是Java并发编程
- **#5-17 Page 480**:对一些代码要求同步,但是【对被检测】到不可能存在共享数据竞争的锁进行消除
<br>更正:对一些代码要求同步,但是【虚拟机又检测】到不可能存在共享数据竞争的锁进行消除
#### 以下勘误内容已在第4次重印版2020-6-10日修正
------
- **#4-1 Page 25**:即不能动态加载其他【编译器】不可知的代码和类库
<br>更正:即不能动态加载其他【编译期】不可知的代码和类库
- **#4-2 Page 30**JDK 7则【还完全】处于研发状态的半成品。
<br>更正JDK 7则【完全是】处于研发状态的半成品。
- **#4-3 Page 38**由于JDK12已EOLRedHat的GitHub上目前只维护8、11两个LTS和13这个正在半年支持期内的JDK版本JDK12的Repo已经关闭但从Git的History中挖掘出来是很容易的为了便于读者获取 CMakeLists.txt我上传了一份到本仓库中。此文件版权归原作者所有。编辑在更新勘误时可跳过此条
- **#4-4 Page 48**Java是一门面向对象的编程语言Java程序运行过程中【无时无刻都】有对象被创建出来
<br>更正Java是一门面向对象的编程语言Java程序运行过程中【每时每刻都】有对象被创建出来
- **#4-5 Page 58-60**代码清单2-4、2-5的输出在代码中定义的方法名是“stackLeak()”在输出日志中看到的堆栈的方法名是“leak()”。这个是代码粘贴后整理编辑过方法名,不影响意思表达。(方法名在日志、代码中出现次数较多,编辑处理这条勘误时请单独联系我提供更正详情)
- **#4-6 Page 80**HotSpot虚拟机里面关注吞吐量的Parallel 【Scavenge】收集器是基于标记-整理算法的。
<br>更正HotSpot虚拟机里面关注吞吐量的Parallel 【Old】收集器是基于标记-整理算法的。
- **#4-7 Page 97**并发回收时垃圾收集线程【只占用不超过25%】的处理器运算资源,并且会随着处理器核心数量的增加而下降。
<br>更正并发回收时垃圾收集线程【占用不少于25%】的处理器运算资源,并且会随着处理器核心数量的增加而下降。
- **#4-8 Page 105**图3-14中第3行G1收集器的Compact阶段应该为【浅色】这个我交上去的原图是正确的但编辑重新画图时候涂层了深色。请编辑更新新版的时候注意一下。
- **#4-9 Page 106**在回收时通过这张表格就可以得出哪些Region之间产生了【跨代引用】。
<br>更正在回收时通过这张表格就可以得出哪些Region之间产生了【跨Region的引用】。
- **#4-10 Page 107**图3-15中两个X的位置打错了应该在5行3列、3行1列处打X
- **#4-11 Page 106**降低了处理跨代指针时的记忆集维护消耗也降低了伪共享问题【见3.4.4节】)的发生概率。
<br>更正降低了处理跨代指针时的记忆集维护消耗也降低了伪共享问题【见3.4.5节】)的发生概率。
- **#4-12 Page 134**如果在Survivor空间中【相同年龄】所有对象大小的总和大于Survivor空间的一半
<br>更正如果在Survivor空间中【低或等于某个年龄的】所有对象大小的总和大于Survivor空间的一半
- **#4-13 Page 134**满足【同年】对象达到Survivor空间的一半规则
<br>更正满足【低于或等于某年龄的】对象达到Survivor空间的一半规则
- **#4-14 Page 162-163**代码中类名“SynAddRunalbe”中英文“Runalbe”为拼写错误应为“SynAddRunnable”代码中一共有4处图片中有1处。
- **#4.15 Page 239**Exceptions属性的作用是列举出方法中可能抛出的受查异常Checked 【Excepitons】
<br>更正Exceptions属性的作用是列举出方法中可能抛出的受查异常Checked 【Exceptions】
- **#4.16 Page 265**创建动作由字节码指令【newarray】触发
<br>更正创建动作由字节码指令【anewarray】触发
- **#4-17 Page 272**【JDK 7及之前】HotSpot使用永久代来实现方法区时实现是完全符合这种逻辑概念的【而在JDK 8及之后】类变量则会随着Class对象一起存放在Java堆中
<br>更正【JDK 7之前】HotSpot使用永久代来实现方法区时实现是完全符合这种逻辑概念的【而在JDK 7及之后】类变量则会随着Class对象一起存放在Java堆中
- **#4-18 Page 274**被访问类C是public的不与访问类D处于同一个模块但是被访问类C的模块允许【被访问类D】的模块进行访问。
<br>更正被访问类C是public的不与访问类D处于同一个模块但是被访问类C的模块允许【访问类D】的模块进行访问。
- **#4-19 Page 312**运行结果的第3行This 【gay】 has $2
<br>更正This 【guy】 has $2
- **#4-20 Page 314**脚注三最后一行具体可【常见】第11章关于方法内联的内容。
<br>更正具体可【参见】第11章关于方法内联的内容。
- **#4-21 Page 461**图12-6New->RunningRunning->Terminated这两个是单向箭头交给编辑的原稿上也还是单向的应该是排版修图时搞错变成双向了请编辑同学修正过来。
- **#4-22 Page 462**:【以前处理一个请求可以允许花费很长时间在单体应用中】,具有这种线程切换成本也是无伤大雅的
<br>语句不通,更正为:【在以前的单体应用中,处理一个请求可以允许花费很长时间】,具有这种线程切换成本也是无伤大雅的
- **#4-23 Page 462**现实的需求在迫使Java去研究新的解决方案【同】大家又怀念起以前绿色线程的种种好处
<br>更正为现实的需求在迫使Java去研究新的解决方案【此时】大家又怀念起以前绿色线程的种种好处
#### 以下勘误内容已在第3次重印版2020-2-20日修正
------
- **#3-1 Page 27**到了JDK 10HotSpot又重构了Java虚拟机的垃圾收集器接口 Java Virtual Machine 【Compiler】 Interface
<br>更正到了JDK 10HotSpot又重构了Java虚拟机的垃圾收集器接口 Java Virtual Machine 【Garbage】 Interface
- **#3-2 Page 37**譬如【Eclipst】 CDT或者NetBeans来进行的话
<br>更正譬如【Eclipse】 CDT或者NetBeans来进行的话
- **#3-3 Page 110**:对象的读取、写入、对象的比较、【为对象哈希值计算】、用对象加锁等等
<br>更正:对象的读取、写入、对象的比较、【为对象计算哈希值】、用对象加锁等等
- **#3-4 Page 113**图3-19中【Larage】为笔误应为【Large】
- **#3-5 Page 238**13: iload_1 后的注释应该是字节码第14行的
> 13: iload_1 // 保存x到returnValue中此时x=2
> 14: istore 4
改为:
> 13: iload_1
> 14: istore 4 // 保存x到returnValue中此时x=2
- **#3-6 Page 254**:算术指令用于对【两个操作数栈上的值】进行某种特定运算
<br>对语序修改以避免歧义:算术指令用于对【操作数栈上的两个值】进行某种特定运算
- **#3-7 Page 259**13 astore_3 后注释【Taget】为笔误应为【Target】
- **#3-8 Page 265/266**在266页正文中出现两次注释一其中第一个注释是265页才对应该是排版问题请编辑再版时注意。
- **#3-9 Page 278**代码清单7-5第一行注释给变量【复制】可以正常编译通过
<br>更正:
给变量【赋值】可以正常编译通过
- **#3-10 Page 278**代码清单7-5第二行注释这句编译器会提示“非法【向前】引用”
<br>更正:
这句编译器会提示“非法【前向】引用”
- **#3-11 Page 290**用在IntelliJ 【IDE】、Eclipse这些IDE上做HotSwap……
<br>更正:
用在IntelliJ 【IDEA】、Eclipse这些IDE上做HotSwap……
- **#3-12 Page 312**代码实例中出现三处【gay】譬如Father gay = new Son(); 均应为【guy】这个不影响代码运行只是不太雅观。
- **#3-13 Page 317**产生这种差别产生的根本原因是Java语言在编译期间【却】已将println(String)方法完整的符号引用。
<br>更正产生这种差别产生的根本原因是Java语言在编译期间【就】已将println(String)方法完整的符号引用。
- **#3-14 Page 322**由于注解中John Rose博客文章中的代码托管网站Kenai.com已经关闭为了便于读者获取INDY工具我上传了一份到本仓库中(源码在src/indify目录)。此文件版权归原作者John Rose所有。编辑在更新勘误时可跳过此条
- **#3-15 Page 348**:通常解决【类】问题有以下几种途径
<br>更正:通常解决【此类】问题有以下几种途径
- **#3-16 Page 372**譬如将【代码清单10-2】稍微修改一下变成下面代码清单10-7这个样子
<br>更正譬如将【代码清单10-4】稍微修改一下变成下面代码清单10-7这个样子
- **#3-17 Page 396**【图11-2】和【图11-3】都仅仅是描述了客户端模式虚拟机的即时编译方式
<br>更正【图11-3】和【图11-4】都仅仅是描述了客户端模式虚拟机的即时编译方式
#### 以下勘误内容已在第2次重印版2019-12-27日修正
------
- **#2.1 前言部分**读者反馈信箱understandjvm@gmail.com
<br>更正由于这个信箱由于一直只收未发刚印刷后收到Google的通知此账号已自动作废。而且根据Google规定作废后无法注册同名邮箱。下次重印将修改为本工程地址:https://github.com/fenixsoft/jvm_book。
- **#2-2 Page 9**支持HTTP 2客户【单】API等91个JEP
<br>更正支持HTTP 2客户【端】API等91个JEP
- **#2-3 Page 64**在【代码清单2-8】里笔者借助了CGLib……
<br>更正在【代码清单2-9】里笔者借助了CGLib……
- **#2.4 Page 368**: 【ArrayList&lt;int&gt;】与ArrayList&lt;String&gt;其实是同一个类型
<br>更正【ArrayList&lt;Integer&gt;】与ArrayList&lt;String&gt;其实是同一个类型