跳至主要內容

元数据

周志明约 6399 字大约 21 分钟...

元数据

[!abstract] 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)

  •  深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)|200
    深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)|200
  • 书名: 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)
  • 作者: 周志明
  • 简介: 内容介绍这是一部从工作原理和工程实践两个维度深入剖析JVM的著作,是计算机领域公认的经典,繁体版在台湾也颇受欢迎。自2011年上市以来,前两个版本累计印刷36次,销量超过30万册,两家主要网络书店的评论近90000条,内容上近乎零差评,是原创计算机图书领域不可逾越的丰碑。第3版在第2版的基础上做了重大修订,内容更丰富、实战性更强:根据新版JDK对内容进行了全方位的修订和升级,围绕新技术和生产实践新增逾10万字,包含近50%的全新内容,并对第2版中含糊、瑕疵和错误内容进行了修正。全书一共13章,分为五大部分:第壹部分(第1章)走近Java系统介绍了Java的技术体系、发展历程、虚拟机家族,以及动手编译JDK,了解这部分内容能对学习JVM提供良好的指引。第二部分(第2~5章)自动内存管理详细讲解了Java的内存区域与内存溢出、垃圾收集器与内存分配策略、虚拟机性能监控与故障排除等与自动内存管理相关的内容,以及10余个经典的性能优化案例和优化方法;第三部分(第6~9章)虚拟机执行子系统深入分析了虚拟机执行子系统,包括类文件结构、虚拟机类加载机制、虚拟机字节码执行引擎,以及多个类加载及其执行子系统的实战案例;第四部分(第10~11章)程序编译与代码优化详细讲解了程序的前、后端编译与优化,包括前端的易用性优化措施,如泛型、主动装箱拆箱、条件编译等的内容的深入分析;以及后端的性能优化措施,如虚拟机的热点探测方法、HotSpot 的即时编译器、提前编译器,以及各种常见的编译期优化技术;第五部分(第12~13章)高效并发主要讲解了Java实现高并发的原理,包括Java的内存模型、线程与协程,以及线程安全和锁优化。全书以实战为导向,通过大量与实际生产环境相结合的案例分析和展示了解决各种Java技术难题的方案和技巧。
  • 出版时间: 2021-05-16 00:00:00
  • ISBN: 9787111641247
  • 分类: 计算机-编程设计
  • 出版社: 机械工业出版社
  • PC地址:https://weread.qq.com/web/reader/cf1320d071a1a78ecf19254open in new window

高亮划线

📌 方法
⏱ 2020-03-02 22:57:17 ^27371406-21-4046-4048

📌 
图2-1 Java虚拟机运行时数据区
2.2.1 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 ^27371406-15-726

  • 💭 这图是jvm的精髓 - ⏱ 2020-02-25 06:28:52

📌 行号指示器
⏱ 2020-02-06 12:28:53 ^27371406-15-937-942

📌 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
⏱ 2020-02-06 12:28:48 ^27371406-15-1110-1148

📌 各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。 ^27371406-15-1354-1393

  • 💭 程序计数器为线程私有的内存 - ⏱ 2020-02-25 06:16:43

📌 此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
⏱ 2020-02-25 06:28:39 ^27371406-15-1510-1561

📌 线程内存模型
⏱ 2020-02-25 06:35:16 ^27371406-15-1740-1746

📌 对象引用
⏱ 2020-02-25 06:37:08 ^27371406-15-2308-2312

📌 局部变量槽(Slot)
⏱ 2020-02-25 06:38:20 ^27371406-15-2464-2475

📌 数据类型
⏱ 2020-02-25 06:36:48 ^27371406-15-2256-2260

📌 编译期间完成分配
⏱ 2020-02-25 06:38:52 ^27371406-15-2538-2546

📌 局部变量
⏱ 2020-02-25 06:36:55 ^27371406-15-2231-2235

📌 本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
⏱ 2020-02-27 02:44:08 ^27371406-15-3283-3342

📌 Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建
⏱ 2020-02-27 02:45:13 ^27371406-15-3663-3693

📌 存放对象实例
⏱ 2020-02-27 02:45:49 ^27371406-15-3706-3712

📌 物理上不连续的内存空间中,但在逻辑上它应该被视为连续的
⏱ 2020-02-27 02:50:16 ^27371406-15-5356-5383

📌 对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间
⏱ 2020-02-27 02:50:30 ^27371406-15-5418-5469

📌 Java堆既可以被实现成固定大小的,也可以是可扩展的
⏱ 2020-02-27 02:51:01 ^27371406-15-5499-5525

📌 ava堆中没有内存
⏱ 2020-02-27 02:51:17 ^27371406-15-5573-5582

📌 无法再扩展
⏱ 2020-02-27 02:51:23 ^27371406-15-5593-5598

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

  • 💭 方法区听起来很怪,以为是存方法名的 - ⏱ 2020-02-27 22:10:35

📌 方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常
⏱ 2020-02-27 22:33:19 ^27371406-15-7259-7297

📌 编译期生成的各种字面量与符号引用
⏱ 2020-02-27 22:34:15 ^27371406-15-7499-7515

📌 类加载后
⏱ 2020-02-27 22:34:34 ^27371406-15-7523-7527

📌 状态复用自己的存储空间 ^27371406-16-5973-5984

  • 💭 复用太妙了 - ⏱ 2020-02-28 23:09:13

📌 对象起始地址必须是8字节的整数倍
⏱ 2020-02-28 23:11:02 ^27371406-16-7843-7859

📌 使用句柄和直接指针
⏱ 2020-02-28 23:11:33 ^27371406-16-8206-8215

📌 (Memory Leak)还是内存溢出(Memory Overflow) ^27371406-17-2714-2750

  • 💭 Java堆出现OOM可能是内存泄漏或内存溢出 - ⏱ 2020-02-29 21:25:03

📌 内存泄漏
⏱ 2020-02-29 21:25:32 ^27371406-17-2710-2714

📌 -Xoss参数
⏱ 2020-02-29 21:28:52 ^27371406-17-3666-3673

📌 -Xss
⏱ 2020-02-29 21:29:10 ^27371406-17-3708-3712

📌 减少最大堆和减少栈容量来换取更多的线程
⏱ 2020-03-01 09:54:43 ^27371406-17-11800-11819

3.1 概述

📌 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时
⏱ 2020-03-02 22:41:26 ^27371406-20-772-812

📌 Java堆和方法
⏱ 2020-03-02 22:45:57 ^27371406-20-1171-1179

📌 可达性分析(Reachability Analysis)算法 ^27371406-21-3370-3400

  • 💭 可达性分析算法 - ⏱ 2020-03-02 23:20:49

📌 “引用链”
⏱ 2020-03-02 23:21:41 ^27371406-21-3482-3487

📌 GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的
⏱ 2020-03-02 23:22:02 ^27371406-21-3543-3576

📌 参数、局部变量、临时变量
⏱ 2020-03-02 22:57:07 ^27371406-21-4055-4067

📌 引用类型静态变量
⏱ 2020-03-02 22:57:51 ^27371406-21-4123-4131

📌 字符串常量池(String Table)里的引用
⏱ 2020-03-02 22:59:24 ^27371406-21-4177-4201

📌 强引用
⏱ 2020-03-03 21:58:51 ^27371406-21-5716-5719

📌 软引用
⏱ 2020-03-03 21:59:07 ^27371406-21-5855-5858

📌 弱引用
⏱ 2020-03-03 22:04:46 ^27371406-21-6017-6020

📌 虚引用
⏱ 2020-03-03 22:05:52 ^27371406-21-6178-6181

📌 垃圾收集器的一致的设计原则 ^27371406-22-1443-1456

  • 💭 垃圾收集器原则 - ⏱ 2020-03-28 04:59:56

📌 针对不同分代的类似名
⏱ 2020-03-28 04:54:20 ^27371406-22-3762-3772

📌 部分收集(Partial GC)
⏱ 2020-03-28 04:54:39 ^27371406-22-3822-3838

📌 ■新生代收集(Minor GC/Young GC)
⏱ 2020-03-28 04:54:58 ^27371406-22-3896-3921

📌 老年代收集(Major GC/
⏱ 2020-03-28 04:55:08 ^27371406-22-3966-3981

📌 混合收集(Mixed GC
⏱ 2020-03-28 04:55:14 ^27371406-22-4121-4134

📌 整堆收集(Full GC
⏱ 2020-03-28 04:55:23 ^27371406-22-4206-4218

📌 让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存
⏱ 2020-04-04 22:09:59 ^27371406-22-7333-7366

📌 根节点枚举与之前提及的整理内存碎片一样会面临相似的“Stop The World”
⏱ 2020-04-06 08:38:00 ^27371406-23-938-979

📌 抢先式中断(Preemptive Suspension)和主动式中断(Voluntary Suspension),
⏱ 2020-04-07 04:37:39 ^27371406-23-3214-3271

📌 安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区域看作被扩展拉伸了的安全点。
⏱ 2020-04-07 04:31:38 ^27371406-23-4857-4966

📌 不必去管这些 ^27371406-23-5022-5028

  • 💭 还是没搞懂安全区域用法是怎么进行垃圾收集的。 - ⏱ 2020-04-07 04:40:17

📌 事实上并不只是新生代、老年代之间才有跨代引用的问题,所有涉及部分区域收集(Partial GC)行为的垃圾收集器
⏱ 2020-04-07 04:47:12 ^27371406-23-5343-5399

📌 记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。
⏱ 2020-04-07 04:43:05 ^27371406-23-5518-5553

📌 卡表就是记忆集的一种具体实现,它定义了记忆集的记录精度、与堆内存的映射关系等。
⏱ 2020-04-07 04:44:14 ^27371406-23-6591-6630

📌 一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,称为这个元素变脏(Dirty),没有则标识为0。
⏱ 2020-04-07 04:46:09 ^27371406-23-7955-8043

📌 哪些卡页内存块中包含跨代指针,把它们加入GC ^27371406-23-8072-8094

  • 💭 卡表中的元素指向的卡页内存块中包含跨代指针,就将该内存块加入GC Roots - ⏱ 2020-04-07 04:52:37

📌 Roots中一并扫描
⏱ 2020-04-07 04:52:29 ^27371406-23-8095-8105

📌 避免大对象的原因是,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好它们,而当复制对象时,大对象就意味着高额的内存复制开销。
⏱ 2020-06-22 21:43:13 ^27371406-27-3930-4016

4.1 概述

📌 但我们在学习工具前,也应当意识到工具永远都是知识技能的一层包装,没有什么工具是“秘密武器”,拥有了就能“包治百病”。 ^27371406-30-703-761

  • 💭 工具永远都是知识技能的一层包装 - ⏱ 2020-06-23 06:47:17

4.2 基础故障处理工具

📌 商业授权工具
⏱ 2020-06-30 06:45:14 ^27371406-31-997-1003

📌 正式支持工具
⏱ 2020-06-30 06:45:25 ^27371406-31-1314-1320

📌 实验性工具
⏱ 2020-06-30 06:45:20 ^27371406-31-1563-1568

📌 本地虚拟机唯一ID
⏱ 2020-06-30 06:50:08 ^27371406-31-3579-3588

📌 符号引用
⏱ 2020-07-08 08:40:37 ^27371406-43-6386-6390

7.1 概述

📌 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制 ^27371406-49-537-621

  • 💭 虚拟机的类加载机制 - ⏱ 2020-07-07 08:13:05

📌 代码清单8-11 单分派和多分派
/**
 * 单分派、多分派演示
 * @author zzm
 */
public class Dispatch {

 static class QQ {}
 static class _360 {}

 public static class Father {
 public void hardChoice(QQ arg) {
 System.out.println("father choose qq");
 }

 public void hardChoice(_360 arg) {
 System.out.println("father choose 360");
 }
 }

 public static class Son extends Father {
 public void hardChoice(QQ arg) {
 System.out.println("son choose qq");
⏱ 2020-07-10 07:56:58 ^27371406-58-16425

📌 因此它们就互相死锁,永远无法完成加载请求了 ^27371406-64-8228-8249

  • 💭 前几年总结过sql的死锁,但早上一时想不起来当时写的例子了,也想不起来怎么造成死锁的,晚上突然想到了一个非常好的例子:用户A和用户B面前有一个苹果🍎和一个梨子🍐,A和B都想吃完,A先拿了苹果🍎吃了一口,B拿梨子🍐吃了一口,A想吃B拿的梨子🍐,但B没吃完不给A,B想吃A的苹果,A没吃完也不给B,这个时候A和B就吵起来了,谁也不给谁,谁都得不到对方的水果,这样就造成了死锁。 - ⏱ 2020-07-13 19:53:24

📌 解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即运行。当程序启动后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,这样可以减少解释器的中间损耗,获得更高的执行效率。当程序运行环境中内存资源限制较大,可以使用解释执行节约内存(如部分嵌入式系统中和大部分的JavaCard应用中就只有解释器的存在),反之可以使用编译执行来提升效率 ^27371406-75-1277-1479

  • 💭 解释执行和编译执行的时间 - ⏱ 2020-07-31 00:03:51

📌 安全点(Safepoint)
⏱ 2020-04-07 04:36:36 ^27371406-23-2813-2827

📌 不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如图12-2所示,注意与图12-1进行对比。

图12-2 线程、主内存、工作内存三者的交互关系(请与图12-1对比)
这里所讲的主内存、工作内存与第2章所讲的Java内存区域中的Java堆、栈、方法区等并不是同一个层次的对内存的划分,这两者基本上是没有任何关系的。如果两者一定要勉强对应起来,那么从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。
⏱ 2020-07-22 15:01:02 ^27371406-83-2364

📌 主内存主要对应于Java堆中的对象实例数据部分[插图],而工作内存则对应于虚拟机栈中的部分区域。从更基础的层次上说
⏱ 2020-08-12 14:43:21 ^27371406-83-2852-3114

📌 主内存直接对应于物理硬件的内存
⏱ 2020-08-12 14:42:53 ^27371406-83-3115-3130

📌 工作内存优先存储于寄存器和高速缓存
⏱ 2020-08-12 14:43:05 ^27371406-83-3170-3187

📌 这段代码发起了20个线程 ^27371406-83-8059-8071

  • 💭 这段示例代码是不是有问题呀?这里应该 Thread.activeCount() >2 才对, Thread.activeCount() 不会出现=1或小于1的情形,因为有main线程和GC线程两个线程。 - ⏱ 2020-08-13 12:28:50

📌 2.可见性(Visibility)
可见性就是指当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。上文在讲解volatile变量的时候我们已详细讨论过这一点。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论是普通变量还是volatile变量都是如此。普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此我们可以说volatile保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。
除了volatile之外,Java还有两个关键字能实现可见性,它们是synchronized和final。同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)”这条规则获得的。而final关键字的可见性是指:被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看见final字段的值。
⏱ 2020-07-22 07:58:01 ^27371406-83-17038

📌 Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的 ^27371406-83-17123-17188

  • 💭 可见性的实现方式,通过主内存作为传递媒介。 - ⏱ 2020-07-22 07:58:01

📌 synchronized的“万能”也间接造就了它被程序员滥用的局面,越“万能”的并发控制,通常会伴随着越大的性能影响 ^27371406-83-18508-18566

  • 💭 任何事物都有正反两面 - ⏱ 2020-07-19 10:13:46

读书笔记

划线评论

📌 ^37992928-7iJ8Yglaj
- 💭 栈帧的概念结构
- ⏱ 2020-07-10 06:33:52

划线评论

📌 ^37992928-7iJ8W1X2C
- 💭 栈帧的基本概念
- ⏱ 2020-07-10 06:33:19

2.2.1 程序计数器

划线评论

📌 
图2-1 Java虚拟机运行时数据区
2.2.1 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 ^37992928-7fogHe62c
- 💭 这图是jvm的精髓
- ⏱ 2020-02-25 06:31:00

划线评论

📌 各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。 ^37992928-7fofYxT9L
- 💭 程序计数器为线程私有的内存
- ⏱ 2020-02-25 06:20:00

2.2.5 方法区

划线评论

📌 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 ^37992928-7fskhKqW9
- 💭 方法区听起来很怪,以为是存方法名的
- ⏱ 2020-02-27 22:32:29

2.3.2 对象的内存布局

划线评论

📌 HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding) ^37992928-7ftSXhJmH
- 💭 对象存储布局
- ⏱ 2020-02-28 23:08:32

划线评论

📌 状态复用自己的存储空间 ^37992928-7ftT3rpWZ
- 💭 复用太妙了
- ⏱ 2020-02-28 23:10:03

2.4.1 Java堆溢出

划线评论

📌 (Memory Leak)还是内存溢出(Memory Overflow) ^37992928-7fviEVX4F
- 💭 Java堆出现OOM可能是内存泄漏或内存溢出
- ⏱ 2020-02-29 21:27:40

3.2.2 可达性分析算法

划线评论

📌 可达性分析(Reachability Analysis)算法 ^37992928-7fysI87Z5
- 💭 可达性分析算法
- ⏱ 2020-03-02 23:21:10

3.3.1 分代收集理论

划线评论

📌 垃圾收集器的一致的设计原则 ^37992928-7gaQG5wXo
- 💭 垃圾收集器原则
- ⏱ 2020-03-28 05:00:36

3.4.3 安全区域

划线评论

📌 不必去管这些 ^37992928-7gq2u69MZ
- 💭 还是没搞懂安全区域用法是怎么进行垃圾收集的。
- ⏱ 2020-04-07 04:40:56

3.4.4 记忆集与卡表

划线评论

📌 哪些卡页内存块中包含跨代指针,把它们加入GC ^37992928-7gq3fZxF1
- 💭 卡表中的元素指向的卡页内存块中包含跨代指针,就将该内存块加入GC Roots
- ⏱ 2020-04-07 04:52:43

3.5.1 Serial收集器

划线评论

📌 你妈妈在给你打扫房间的时候,肯定也会让你老老实实地在椅子上或者房间外待着,如果她一边打扫,你一边乱扔纸屑,这房间还能打扫完? ^37992928-7gNhmV80T
- 💭 比喻很形象
- ⏱ 2020-04-22 11:21:37

4.1 概述

划线评论

📌 但我们在学习工具前,也应当意识到工具永远都是知识技能的一层包装,没有什么工具是“秘密武器”,拥有了就能“包治百病”。 ^37992928-7ijiCOKEA
- 💭 工具永远都是知识技能的一层包装
- ⏱ 2020-06-23 06:47:50

6.3.2 常量池

划线评论

📌 符号引用 ^37992928-7iGeFOIxH
- 💭 符号引用总是忘记是干啥的。
- ⏱ 2020-07-08 08:40:55

7.1 概述

划线评论

📌 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制 ^37992928-7iEGycbqj
- 💭 虚拟机的类加载机制
- ⏱ 2020-07-07 08:13:14

7.2 类加载的时机

划线评论

📌 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称为连接(Linking)。 ^37992928-7iESsZvHH
- 💭 类型加载的生命周期
- ⏱ 2020-07-07 11:15:11

9.2.2 OSGi:灵活的类加载器架构

划线评论

📌 因此它们就互相死锁,永远无法完成加载请求了 ^37992928-7iOyF1MG2
- 💭 前几年总结过sql的死锁,但早上一时想不起来当时写的例子了,也想不起来怎么造成死锁的,晚上突然想到了一个非常好的例子:用户A和用户B面前有一个苹果🍎和一个梨子🍐,A和B都想吃完,A先拿了苹果🍎吃了一口,B拿梨子🍐吃了一口,A想吃B拿的梨子🍐,但B没吃完不给A,B想吃A的苹果,A没吃完也不给B,这个时候A和B就吵起来了,谁也不给谁,谁都得不到对方的水果,这样就造成了死锁。
- ⏱ 2020-07-13 19:59:28

11.2.1 解释器与编译器

划线评论

📌 解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即运行。当程序启动后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,这样可以减少解释器的中间损耗,获得更高的执行效率。当程序运行环境中内存资源限制较大,可以使用解释执行节约内存(如部分嵌入式系统中和大部分的JavaCard应用中就只有解释器的存在),反之可以使用编译执行来提升效率 ^37992928-7jeFXB7Lp
- 💭 解释执行和编译执行的时间
- ⏱ 2020-07-31 00:04:22

12.3.3 对于volatile型变量的特殊规则

划线评论

📌 这段代码发起了20个线程 ^37992928-7jzeRLGmn
- 💭 这段示例代码是不是有问题呀?这里应该 Thread.activeCount() >2 才对, Thread.activeCount() 不会出现=1或小于1的情形,因为有main线程和GC线程两个线程。
- ⏱ 2020-08-13 12:30:45

12.3.5 原子性、可见性与有序性

划线评论

📌 Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的 ^37992928-7j1ucAHAU
- 💭 可见性的实现方式,通过主内存作为传递媒介。
- ⏱ 2020-07-22 07:58:08

划线评论

📌 synchronized的“万能”也间接造就了它被程序员滥用的局面,越“万能”的并发控制,通常会伴随着越大的性能影响 ^37992928-7iX4bn995
- 💭 任何事物都有正反两面
- ⏱ 2020-07-19 10:14:08

13.3.5 偏向锁

划线评论

📌 当一个对象已经计算过一致性哈希码后,它就再也无法进入偏向锁状态了 ^37992928-7iXulZOo3
- 💭 这里为什么再也无法进入?下次再用偏向锁不行吗?
- ⏱ 2020-07-19 16:53:45

本书评论

书评 No.1

^37992928-7iC84CwKY
⏱ 2020-07-05 15:53:28

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.3.0