深入理解Java虚拟机的目录
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Java虚拟机的目录相关的知识,希望对你有一定的参考价值。
前言
致谢
第一部分 走近Java
第1章 走近Java / 2
1.1 概述 / 2
1.2 Java技术体系 / 3
1.3 Java发展史 / 5
1.4 展望Java技术的未来 / 9
1.4.1 模块化 / 9
1.4.2 混合语言 / 9
1.4.3 多核并行 / 11
1.4.4 进一步丰富语法 / 12
1.4.5 64位虚拟机 / 13
1.5 实战:自己编译JDK / 13
1.5.1 获取JDK源码 / 13
1.5.2 系统需求 / 14
1.5.3 构建编译环境 / 15
1.5.4 准备依赖项 / 17
1.5.5 进行编译 / 18
1.6 本章小结 / 21
第二部分 自动内存管理机制
第2章 Java内存区域与内存溢出异常 / 24
2.1 概述 / 24
2.2 运行时数据区域 / 25
2.2.1 程序计数器 / 25
2.2.2 Java虚拟机栈 / 26
2.2.3 本地方法栈 / 27
2.2.4 Java堆 / 27
2.2.5 方法区 / 28
2.2.6 运行时常量池 / 29
2.2.7 直接内存 / 29
2.3 对象访问 / 30
2.4 实战:OutOfMemoryError异常 / 32
2.4.1 Java堆溢出 / 32
2.4.2 虚拟机栈和本地方法栈溢出 / 35
2.4.3 运行时常量池溢出 / 38
2.4.4 方法区溢出 / 39
2.4.5 本机直接内存溢出 / 41
2.5 本章小结 / 42
第3章 垃圾收集器与内存分配策略 / 43
3.1 概述 / 43
3.2 对象已死? / 44
3.2.1 引用计数算法 / 44
3.2.2 根搜索算法 / 46
3.2.3 再谈引用 / 47
3.2.4 生存还是死亡? / 48
3.2.5 回收方法区 / 50
3.3 垃圾收集算法 / 51
3.3.1 标记 -清除算法 / 51
3.3.2 复制算法 / 52
3.3.3 标记-整理算法 / 54
3.3.4 分代收集算法 / 54
3.4 垃圾收集器 / 55
3.4.1 Serial收集器 / 56
3.4.2 ParNew收集器 / 57
3.4.3 Parallel Scavenge收集器 / 59
3.4.4 Serial Old收集器 / 60
3.4.5 Parallel Old收集器 / 61
3.4.6 CMS收集器 / 61
3.4.7 G1收集器 / 64
3.4.8 垃圾收集器参数总结 / 64
3.5 内存分配与回收策略 / 65
3.5.1 对象优先在Eden分配 / 66
3.5.2 大对象直接进入老年代 / 68
3.5.3 长期存活的对象将进入老年代 / 69
3.5.4 动态对象年龄判定 / 71
3.5.5 空间分配担保 / 73
3.6 本章小结 / 75
第4章 虚拟机性能监控与故障处理工具 / 76
4.1 概述 / 76
4.2 JDK的命令行工具 / 76
4.2.1 jps:虚拟机进程状况工具 / 79
4.2.2 jstat:虚拟机统计信息监视工具 / 80
4.2.3 jinfo:Java配置信息工具 / 82
4.2.4 jmap:Java内存映像工具 / 82
4.2.5 jhat:虚拟机堆转储快照分析工具 / 84
4.2.6 jstack:Java堆栈跟踪工具 / 85
4.3 JDK的可视化工具 / 87
4.3.1 JConsole:Java监视与管理控制台 / 88
4.3.2 VisualVM:多合一故障处理工具 / 96
4.4 本章小结 / 105
第5章 调优案例分析与实战 / 106
5.1 概述 / 106
5.2 案例分析 / 106
5.2.1 高性能硬件上的程序部署策略 / 106
5.2.2 集群间同步导致的内存溢出 / 109
5.2.3 堆外内存导致的溢出错误 / 110
5.2.4 外部命令导致系统缓慢 / 112
5.2.5 服务器JVM进程崩溃 / 113
5.3 实战:Eclipse运行速度调优 / 114
5.3.1 调优前的程序运行状态 / 114
5.3.2 升级JDK 1.6的性能变化及兼容问题 / 117
5.3.3 编译时间和类加载时间的优化 / 122
5.3.4 调整内存设置控制垃圾收集频率 / 126
5.3.5 选择收集器降低延迟 / 130
5.4 本章小结 / 133
第三部分 虚拟机执行子系统
第6章 类文件结构 / 136
6.1 概述 / 136
6.2 无关性的基石 / 136
6.3 Class类文件的结构 / 138
6.3.1 魔数与Class文件的版本 / 139
6.3.2 常量池 / 141
6.3.3 访问标志 / 147
6.3.4 类索引、父类索引与接口索引集合 / 148
6.3.5 字段表集合 / 149
6.3.6 方法表集合 / 153
6.3.7 属性表集合 / 155
6.4 Class文件结构的发展 / 168
6.5 本章小结 / 170
第7章 虚拟机类加载机制 / 171
7.1 概述 / 171
7.2 类加载的时机 / 172
7.3 类加载的过程 / 176
7.3.1 加载 / 176
7.3.2 验证 / 178
7.3.3 准备 / 181
7.3.4 解析 / 182
7.3.5 初始化 / 186
7.4 类加载器 / 189
7.4.1 类与类加载器 / 189
7.4.2 双亲委派模型 / 191
7.4.3 破坏双亲委派模型 / 194
7.5 本章小结 / 197
第8章 虚拟机字节码执行引擎 / 198
8.1 概述 / 198
8.2 运行时栈帧结构 / 199
8.2.1 局部变量表 / 199
8.2.2 操作数栈 / 204
8.2.3 动态连接 / 206
8.2.4 方法返回地址 / 206
8.2.5 附加信息 / 207
8.3 方法调用 / 207
8.3.1 解析 / 207
8.3.2 分派 / 209
8.4 基于栈的字节码解释执行引擎 / 221
8.4.1 解释执行 / 221
8.4.2 基于栈的指令集与基于寄存器的指令集 / 223
8.4.3 基于栈的解释器执行过程 / 224
8.5 本章小结 / 230
第9章 类加载及执行子系统的案例与实战 / 231
9.1 概述 / 231
9.2 案例分析 / 231
9.2.1 Tomcat:正统的类加载器架构 / 232
9.2.2 OSGi:灵活的类加载器架构 / 235
9.2.3 字节码生成技术与动态代理的实现 / 238
9.2.4 Retrotranslator:跨越JDK版本 / 242
9.3 实战:自己动手实现远程执行功能 / 246
9.3.1 目标 / 246
9.3.2 思路 / 247
9.3.3 实现 / 248
9.3.4 验证 / 255
9.4 本章小结 / 256
第四部分 程序编译与代码优化
第10章 早期(编译期)优化 / 258
10.1 概述 / 258
10.2 Javac编译器 / 259
10.2.1 Javac的源码与调试 / 259
10.2.2 解析与填充符号表 / 262
10.2.3 注解处理器 / 264
10.2.4 语义分析与字节码生成 / 264
10.3 Java语法糖的味道 / 268
10.3.1 泛型与类型擦除 / 268
10.3.2 自动装箱、拆箱与遍历循环 / 273
10.3.3 条件编译 / 275
10.4 实战:插入式注解处理器 / 276
10.4.1 实战目标 / 276
10.4.2 代码实现 / 277
10.4.3 运行与测试 / 284
10.4.4 其他应用案例 / 286
10.5 本章小结 / 286
第11章 晚期(运行期)优化 / 287
11.1 概述 / 287
11.2 HotSpot虚拟机内的即时编译器 / 288
11.2.1 解释器与编译器 / 288
11.2.2 编译对象与触发条件 / 291
11.2.3 编译过程 / 294
11.2.4 查看与分析即时编译结果 / 297
11.3 编译优化技术 / 301
11.3.1 优化技术概览 / 301
11.3.2 公共子表达式消除 / 305
11.3.3 数组边界检查消除 / 307
11.3.4 方法内联 / 307
11.3.5 逃逸分析 / 309
11.4 Java与C/C++的编译器对比 / 311
11.5 本章小结 / 313
第五部分 高效并发
第12章 Java内存模型与线程 / 316
12.1 概述 / 316
12.2 硬件的效率与一致性 / 317
12.3 Java内存模型 / 318
12.3.1 主内存与工作内存 / 319
12.3.2 内存间交互操作 / 320
12.3.3 对于volatile型变量的特殊规则 / 322
12.3.4 对于long和double型变量的特殊规则 / 327
12.3.5 原子性、可见性与有序性 / 328
12.3.6 先行发生原则 / 330
12.4 Java与线程 / 333
12.4.1 线程的实现 / 333
12.4.2 Java线程调度 / 337
12.4.3 状态转换 / 339
12.5 本章小结 / 341
第13章 线程安全与锁优化 / 342
13.1 概述 / 342
13.2 线程安全 / 343
13.2.1 Java语言中的线程安全 / 343
13.2.2 线程安全的实现方法 / 348
13.3 锁优化 / 356
13.3.1 自旋锁与自适应自旋 / 356
13.3.2 锁消除 / 357
13.3.3 锁粗化 / 358
13.3.4 轻量级锁 / 358
13.3.5 偏向锁 / 361
13.4 本章小结 / 362
附录A Java虚拟机家族 / 363
附录B 虚拟机字节码指令表 / 366
附录C HotSpot虚拟机主要参数表 / 372
附录D 对象查询语言(OQL)简介 / 376
附录E JDK历史版本轨迹 / 383
主要介绍java概述,干货不多看原文。
第二部分 :自动内存管理机制
第二章:java内存区域与内存溢出异常
运行时数据区
虚拟机对象探秘
内存溢出异常分析
第三章 垃圾收集器与内存分配策略
垃圾回收算法
内存分配与回收策略
第四章 虚拟机性能监控与故障处理工具
jstat
jmap
MAT
这块原书作者列举还有Jconsle等,我没整理全。
第五章 调优案例分析 :这章还是看书吧。
第三部分 虚拟机执行子系统
第六章 类文件结构
第七章虚拟机类加载机制
第八章 虚拟机字节码执行引擎
第九章 类加载及执行子系统案例与实战:参看原书
第四部分 程序编译与代码优化
第10章 早期(编译器)优化
第11章 晚期(运行期)优化,待补充
第五部分 高效并发
此部分认为原书写的偏简单,打算独立出来整理。 参考技术B 第一章
主要介绍java概述,干货不多看原文。
第二部分 :自动内存管理机制
第二章:java内存区域与内存溢出异常
运行时数据区
虚拟机对象探秘
内存溢出异常分析
第三章 垃圾收集器与内存分配策略
垃圾回收算法
内存分配与回收策略
第四章 虚拟机性能监控与故障处理工具
jstat
jmap
MAT
这块原书作者列举还有Jconsle等,我没整理全。
第五章 调优案例分析 :这章还是看书吧。
第三部分 虚拟机执行子系统
第六章 类文件结构
第七章虚拟机类加载机制
第八章 虚拟机字节码执行引擎
第九章 类加载及执行子系统案例与实战:参看原书
第四部分 程序编译与代码优化
第10章 早期(编译器)优化
第11章 晚期(运行期)优化,待补充
第五部分 高效并发
深入理解JVM(③)虚拟机的类加载时机
前言
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制。
类加载的时机
一个类型从被加载到虚拟机内存中开始,到卸载除内存为止,它的生命周期将会经历加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和 卸载(Unloading)、七个阶段,其中验证、准备、解析三个部分统称为连接(Linking)。
类的生命周期如下图:
其实加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语音的运行时绑定特性(也称为动态绑定或晚期绑定)。
在什么情况下需要开始类加载过程的第一个阶段“加载”,《Java虚拟机规则》中并没有进行强制约束,但是对于初始化阶段《Java虚拟机规范》则是严格规定了有且只有以下六种情况必须立即对类进行“初始化”。
- 遇到
new
、getstatic
、putstatic
或invokestatic
这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化阶段。
涉及到这四条指令的典型场景有:
- 使用new关键字实例化对的时候。
- 读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候。
- 调用一个类型的静态方法的时候。
- 使用
java.lang.reflect
包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化。 - 当初始化类型的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
- 当使用JDK7新加入的动态语言支持时,如果一个
java.lang.invoke.MethodHandle
实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。 - 当一个接口中定义了JDK8新加入的默认方法(被
default
关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
除了以上的这个六种场景外,所有引用类型的方式都不会触发初始化,称为被动引用。
下面来看一下哪些是被动引用:
例子??1:
父类
package com.jimoer.classloading;
/**
* @author jimoer
* @date Create in 2020/06/24 16:08
* @description 通过子类引用父类的静态字段,不会导致子类初始化。
*/
public class FatherClass {
static {
System.out.println("FatherClass init!!!!!");
}
public static int value = 666;
}
子类
package com.jimoer.classloading;
public class SonClass extends FatherClass{
static {
System.out.println("SonClass init!!!");
}
}
测试类
@Test
public void testInitClass(){
System.out.println(SonClass.value);
}
运行结果:
FatherClass init!!!!!
666
通过运行结果我们看到,只输出了“FatherClass init!!!!!”,并没有输出“SubClass init!!!”,这是因为对于使用静态字段,只有直接定义这个字段的类才会被初始化,因此通过子类来引用父类中定义的静态字段,并不会初始化子类。
例子??2:
/**
* 通过数组定义来引用类,不会触发此类的初始化
*/
@Test
public void testInitClass2(){
FatherClass[] fathers = new FatherClass[5];
}
运行结果:未打印任何信息。
通过运行结果我们发现,并没有打印出 FatherClass init!!!!! ,这说明并没有触发Father类的初始化阶段。但是这段代码里面触发了另一个名为“[Lcom.jimoer.classloading.FatherClass
”的类的初始化阶段,它是一个由虚拟机自动生成的、直接继承与java.lang.Object
的子类,创建动作由字节码newarray触发。这个类代表了一个元素类型为FatherClass的一维数组,数组中应用的属性和方法(length属性和clone()方法)都实现在这个类里。
例子??3:
/**
* @author jimoer
* 常量在编译阶段会存入调用类的常量池中,
* 本质上没有直接引用到定义常量的类,
* 因此不会触发定义常量的类的初始化。
*/
public class ConstantClass {
static {
System.out.println("ConstantClass init !!!");
}
public static final String CLASS_LOAD = "class load test !!!";
}
使用
/**
* 使用常量
*/
@Test
public void testInitClass3(){
System.out.println(ConstantClass.CLASS_LOAD);
}
运行结果:
class load test !!!
通过运行结果,我们看到当在使用一个类的常量时,并不会初始化定义了常量的类。这是因为虽然在Java源码中确实引用了ConstatClass
的类的常量CLASS_LOAD
,但其实在编译阶段通过常量传播优化,已经将此常量的值“class load test !!!
”直接存储在使用常量的类中的常量池中了,所以在使用ConstantClass.CLASS_LOAD
时候,实际上都被转化为在使用类自身的常量池的引用了。
接口也是有初始化过程的,上面的代码都是用静态语句块“static {}”来输出初始化信息的,而接口中不能使用static{}语句块,但编译器仍然会为接口生成“
还有一点接口与类不同,当一个类在初始化时,要求其父类全部都已经初始化过了,但是在一个接口初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(例如引用接口中的常量)才会初始化。
以上是关于深入理解Java虚拟机的目录的主要内容,如果未能解决你的问题,请参考以下文章