深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)读书笔记

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)读书笔记相关的知识,希望对你有一定的参考价值。

前言

我在读 深入理解java虚拟机 这本书,把整体其中的关键点标记了,希望自己对它有个不一样的理解,也希望大家能看看这本写的很好的书

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

pdf提取码:hdiu 

目录

前言

该本书分为下面几部分

 第一部分 走进java 

第二部分 自动内存管理

第三部分 虚拟机执行子系统

第四部分 程序编译与代码优化

第五部分 高效并发

第一部分 走近Java

第1章 走近Java

1.1概述

1.2java技术体系

1.3 Java发展史

1.4 Java虚拟机家族

1.4.2 武林盟主:HotSpot VM

1.4.3 小家碧玉:Mobile/Embedded VM

1.4.4 天下第二:BEA JRockit/IBM J9 VM

1.4.5 软硬合璧:BEA Liquid VM/Azul VM

1.4.6 挑战者:Apache Harmony/Google Android Dalvik VM

1.4.7 没有成功,但并非失败:Microsoft JVM及其他

1.4.8 百家争鸣

1.5.2 新一代即时编译器

1.6 实战:自己编译JDK

1.6.1 获取源码

1.6.3 构建编译环境

第二部分 自动内存管理

第2章 Java内存区域与内存溢出异常

2.1 概述

2.2 运行时数据区域

2.3 HotSpot虚拟机对象探秘

2.4 实战:OutOfMemoryError异常

第3章 垃圾收集器与内存分配策略

3.1 概述

3.2 对象已死?

3.3 垃圾收集算法

3.4 HotSpot的算法细节实现

3.5 经典垃圾收集器

3.6 低延迟垃圾收集器

3.7 选择合适的垃圾收集器

3.8 实战:内存分配与回收策略

3.9 本章小结

第4章 虚拟机性能监控、故障处理工具

4.1 概述

4.2 基础故障处理工具

4.3 可视化故障处理工具

4.4 HotSpot虚拟机插件及工具

第5章 调优案例分析与实战

5.1 概述

5.2 案例分析

5.3 实战:Eclipse运行速度调优

5.4 本章小结

第三部分 虚拟机执行子系统

第6章 类文件结构

6.1 概述

6.2 无关性的基石

6.3 Class类文件的结构

6.4 字节码指令简介

6.5 公有设计,私有实现

6.6 Class文件结构的发展

6.7 本章小结

第7章 虚拟机类加载机制

7.1 概述

7.2 类加载的时机

7.3 类加载的过程

7.4 类加载器

7.5 Java模块化系统

7.6 本章小结

第8章 虚拟机字节码执行引擎

8.1 概述

8.2 运行时栈帧结构

8.3 方法调用

8.4 动态类型语言支持

8.5 基于栈的字节码解释执行引擎

8.6 本章小结

第9章 类加载及执行子系统的案例与实战

9.1 概述

9.2 案例分析

9.3 实战:自己动手实现远程执行功能

9.4 本章小结

第四部分 程序编译和代码优化

第10章 前端编译与优化

10.1 概述

10.2 Javac编译器

10.3 Java语法糖的味道

10.4 实战:插入式注解处理器

10.5 本章小结

第11章 后端编译与优化

11.1 概述

11.2 即时编译器

11.3 提前编译器

11.4 编译器优化技术

11.5 实战:深入理解Graal编译器

11.6 本章小结

第五部分 高效并发

第12章 Java内存模型与线程

12.1 概述

12.2 硬件的效率与一致性

12.3 Java内存模型

12.4 Java与线程

12.5 Java与协程

12.6 本章小结

第13章 线程安全与锁优化

13.1 概述

13.2 线程安全

13.3 锁优化

13.4 本章小结

总结



该本书分为下面几部分

 第一部分 走进java 

       第一章 主要介绍了Java技术体系过去、现在的情况以及未来的发展趋势;并介绍如何实现openjdk12

第二部分 自动内存管理

  •     第二章 虚拟机内存划分,各个区域出现内存溢出异常的常见原因
  •     第三章  垃圾收集算法和hotspot虚拟机提供的垃圾收集器
  •     第四章  jdk发布的基础命令行工具与可视化的故障处理工具的使用方法
  •     第五章  分享实际案例

第三部分 虚拟机执行子系统

  • 第六章 讲解class文件结构的各个部分 ,演示class的数据是如何存储访问的
  •  第七章 介绍类的加载 验证 准备 解析  初始化 五个阶段中虚拟机 分别进行了哪些动作
  • 第八章 分析了虚拟机在执行代码 ,如何找到正确的方法、如何执行方法内的字节码
  • 第九章 通过几个类加载执行子系统的案例中,哪些值得借鉴的地方

第四部分 程序编译与代码优化

  • 第十章 分析了java中泛型 、主动装箱 拆箱条件编译等多种语法的前因后果
  • 第十一章 讲解了虚拟机热点探测方法 、hotspot即时编译器、编译触发条件

第五部分 高效并发

  • 第十二章 讲解了虚拟机java内存模型的结构及操作,以及原子性。可见性和有序性在java内存模型种的体现
  • 第十三章 介绍了线程安全所涉及的概念和分类。介绍虚拟机在高并发的情况所做的锁一系列优化措施

第一部分 走近Java

1章 走近Java

1.1概述

java不仅仅是一门编程语言,它还是一个由一系列计算机软件和规范组成的技术体系这个体系提供了完整的用于软件开发和跨平台部署支撑 ;据不完全统计全世界大概有两千三百多万程序从事者,而java程序员就占600多万,这都是得益于这个技术体系而得到的支撑并广泛应用于嵌入式系统、移动终端、企业服务器、大型机等多种场合。

1-1 Java 技术的广泛应用

不可忽视的优点

  •  实现了一次编写,到处运行的理想
  • 它提供一种相对安全的内存管理和访问机制,避免了绝大部分内存泄露和指针越界的问题
  • 他实现了热点代码检测和运行时编译优化,是的java长时间运行时,代码会得到更高性能,但在某种情况下会出现问题,例如多线程的情况下,这个在开发过程中也会提供解决办法

1.2java技术体系

从广义上来讲KotlinClojureJRubyGroovy等运行在java虚拟机上的编程语言及其相关程序都属于java程序中一员;

  • Java各个组成部分的功能来进行划分

jdk(Java Development Kit)是什么

Java程序设计语言、java虚拟机、java类库共同组成jdk;JDK是用于支持Java程序开发的最小环境  

JREJava Runtime Environment)是什么
把java的类库api中的java se api子集和java虚拟机这两部分的统称,JRE 是支持 Java 程序运行的标准环境。
1-2 Java 技术体系所包括的内容
  • 按技术体系来划分
·Java Card:支持 Java 小程序( Applets )运行在小内存设备(如智能卡)上的平台
Java ME: 支持 Java 程序运行在移动终端(手机、 PDA )上的平台,这条线是指之前像10年之前的诺基亚手机,而并不是现在的android手机
Java SE:支持面向桌面级应用(如 Windows 下的应用程序)的 Java 平台 ,而这条线则被大量使用在服务器等等
·Java EE:支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的Java平台,基本被抛弃
java.* 为包名的包都是 Java SE API 的核心包,

1.3 Java发展史

1991 年4月, Oak 开始开发 ,1995年开始 自己发展的市场定位并蜕变成为Java语言

                           图 1-3 Java 技术发展的时间线
1995 5 23 日, 正式命令为java 后面一直在迭代更新;
1999 4 27 日, HotSpot 虚拟机诞生。
2006 12 11日 ,JDK 6 发布
2014 3 18 日 JDK 8的 开发 
整个发展阶段 分为sun公司阶段 和oracle公司阶段, 
Sun 掌舵的前十几年里, Java 获得巨大成
功,同时也渐渐显露出来语言演进的缓慢与社区决策的老朽;而在 Oracle 主导 Java 后,引起竞争的同时 也带来新的活力, Java 发展的速度要显著高于 Sun 时代。

1.4 Java虚拟机家族

1.4.1 虚拟机始祖:Sun Classic/Exact VM
     Classic VM,这款虚拟机只能使用纯解释器方式来执行 Java代码;也就是说当需要使用即时编译器那就必须外挂,但是假如外挂了即时编译器的话,即时编译器就会完全接管虚拟机的执行系统,解释器便不能再工作了。
JDK 1.2 及之前,用户用 Classic 虚拟机执行 java-version 命令

编译与解释 的区别   也是在于 编译是将代码一次性编译出来,生成二进制文件,然后在计算机上直接运行速度很快,而解释,只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的. 

其中的 “sunwjit” Sun Workshop JIT )就是 Sun提供的外挂编译器 ,Symantec JIT shuJIT等 ,为什么会说出java语言很慢,因为用了sunwjit 编译执行器,也会每一行代码都进行编译,执行效率都慢
在JDK 1.2时 ,Solaris平台上发布过一款名为Exact VM的虚拟机 ,热点探测、两级即时编译器、编译器与解释器混合工作模式等 该 虚拟机 为后面虚拟机做了铺垫,   因为它使用了内存准确管理而得名,准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。地址的引用类型还是一个数值为 123456的整数,准确分辨出哪些内存是引用类型
JDK 1.3 时, HotSpot VM成为默认虚拟机 ,它仍作为虚拟机的“备用选择”发布,可以使用 java-classic参数切换 

1.4.2 武林盟主:HotSpot VM

hotspot  虚拟机  和exact vm  最开始都很难选择那个虚拟机作为基准,HotSpot虚拟机的热点代码探测能力可以通过执行计数器找出最具有编译价值的代码,然后通知即时编译器以方法为单位进行编译。 如果一个方法被频繁调用,或方法中有效循环次数很多,将会分别触发即时编译和栈上替换编译(on-stack和replacement osr )行为。

得益于 Sun/OracleJDK Java 应用中的统治地位, HotSpot 理所当然地成为全世界使用最广泛的 Java
虚拟机,是虚拟机家族中毫无争议的 武林盟主

1.4.3 小家碧玉:Mobile/Embedded VM

Java ME 中的java 虚拟机 应用在移动端的。原本它提出的卖点是智能手机上的跨平台 ,需要在开发应用中添加CDC 虚拟机,也就是移动端应用的虚拟机;

1.4.4 天下第二:BEA JRockit/IBM J9 VM

bea system公司开发的 jrockit 和ibm公司开发 的j9;JRockit虚拟机曾经号称是世界上速度最快的Java虚拟机”. 这个速度快的原因在于它不包含解释器的实现,全部代码靠即时编译后执行。BEA将其发展为一款专门为服务器硬件和服务端应用场景高度优化的虚拟机,由于专注于服务端应用;

JRockit 随着 BEA Oracle 收购,现已不再
继续发展,永远停留在 R28 版本,这是 JDK 6 JRockit 的代号。
IBM主力发展无疑就是J9虚拟机
IBM J9 虚拟机的职责分离与模块化做得比 HotSpot 更优秀
J9 虚拟机中抽象封装出来的核心组件库(包括垃圾收集器、即时编译器、诊断监控子系统等)就单独构 成了IBM OMR 项目

1.4.5 软硬合璧:BEA Liquid VM/Azul VM

Zing 虚拟机是一个从 HotSpot 某旧版代码分支基础上独立出来重新开发的高性能 Java虚拟机。
代码热点到对象分配监控、锁 竞争监控等。Zing 能让普通用户无须了解垃圾收集等底层调优,就可以使得 Java 应用享有低延迟、快 速预热、易于监控的功能,这是Zing 的核心价值和卖点

1.4.6 挑战者:Apache Harmony/Google Android Dalvik VM

Dalvik 虚拟机并不是一个 Java 虚拟机,它没有遵循《 Java虚拟机规范》;不能直接执行Java的
Class 文件,使用寄存器架构而不是 Java虚拟机中常见的栈架构。它执行的DEX(Dalvik Executable)文件可以通过Class文件转化而来,使用Java语法编写应用程序
就因为 这个程序,导致google被告。

1.4.7 没有成功,但并非失败:Microsoft JVM及其他

介绍 jvm在Windows 版本上支持

1.4.8 百家争鸣

·KVM ·Java Card VM Squawk VM ·JavaInJava

1.5.2 新一代即时编译器

Hotspot虚拟机中含有两个即时编译器

  • 编译耗时段,但输出代码优化程度较低的客户端编译器简称C1
  • 编译耗时长,但输出代码优化质量高的客户端编译器简称C2

通常他们与解释器相互协作配合共同构成hotspot虚拟机的执行子系统

JDK 10 起, HotSpot 中又加入了一个全新的即时编译器: Graal 编译器

1.6 实战:自己编译JDK

1.6.1 获取源码

openjdk和oraclejdk之间的关系

1-7 OpenJDK OracleJDK 之间的关系
到了 JDK 10 及以后的版本,在组织上出现了一些新变化,此时全部开发工作统一归属到 JDK
JDK Updates 两条主分支上,主分支不再带版本号,在内部再用子分支来区分具体的 JDK 版本。
OpenJDK 12

1-8 OpenJDK 版本之间的关系
获取 OpenJDK 源码有两种方式。一是通过 Mercurial 代码版本管理工具从 Repository 中直接取得源
码( Repository 地址: https://hg.openjdk.java.net/jdk/jdk12 ),zip中直接下载
笔者建议尽量在 Linux 或者 MacOS 上构建 OpenJDK 

1-9 JDK 12 的根目录

在 x86 上构建

至少,建议使用 2-4 个内核的机器以及 2-4 GB 的 RAM。(使用的内核越多,您需要的内存就越多。)至少需要 6 GB 的可用磁盘空间(在 Solaris 上构建最少需要 8 GB)。

即使对于 32 位构建,也建议使用 64 位构建机器,而使用--with-target-bits=32.

1.6.3 构建编译环境

Ubuntu 里用户可以自行选择安装 GCC CLang 来进行编译
Ubuntu 系统上安装 GCC 的命令 为

1-1 OpenJDK 编译依赖库
Ubuntu 中使用以下命令安装 OpenJDK 11

编译调试功能我放到后面进行继续做,

第二部分 自动内存管理

2 Java 内存区域与内存溢出异常
· 3 章 垃圾收集器与内存分配策略
· 4 章 虚拟机性能监控、故障处理工具
· 5 章 调优案例分析与实战

2Java内存区域与内存溢出异常

2.1 概述

在java程序员中,在虚拟机自动内存管理机制的帮助下,像是一堵墙,隔绝开对内存的管理,一般不容易出现内存泄露等,一旦出现内存泄漏,则非常不好解决

2.2 运行时数据区域

2-1 Java 虚拟机运行时数据区

这就是在程序运行时在虚拟机中运行的数据区

公共的区域 方法区和堆  私有的 程序计数器 本地方法栈 虚拟机栈

2.2.1 程序计数器

程序计数器是一块较小的空间,它可以看作是当前线程所执行的字节码行号指示器,字节码解释器就是通过改变该值来选取下个需要执行的字节码指令,它是程序控制流的指示器、分支 循环 、异常处理、线程恢复都依赖它。

每个线程都有一个独立的程序计数器,这个在运行时,保证线程间来回切换;恢复到正确的执行位置,线程封闭,让我想起了threadlocal,但不是一个概念哈;

2.2.2 Java虚拟机栈
它的生命周期与线程相同,虚拟机栈描述的是,java方法执行的线程内存模型(jmm),每个方法执行时,java虚拟机都会为同步创建栈栈帧,用于存储局部变量,操作树栈,动态链接、方法出口等信息
局部变量表存放编译器可知的各种java虚拟机基本数据类型
这些数据类型在局部变量表中的存储空间以局部变量槽( Slot)来表示,其中64位长度的long和
double 类型的数据会占用两个变量槽,其余的数据类型只占用一个。 实现一个变量槽是固定,然后不会被概念的
2.2.3 本地方法栈

基本与java虚拟机栈一致

2.2.4 Java堆
java堆是垃圾收集器管理的内存区域,因此在一些资料中它被称为GC堆,hotspot里面也出现了不采用分代设计的新垃圾收集器,强调可以使用非连续的内存空间
2.2.5 方法区

方法区和java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息,常量 静态变量 即时编译器编译后的代码缓存数据;在hotspot虚拟机中,使用永久代去实现方法区,为什么采用这种方式,也是由于hotspot的垃圾收集器能够像管理java堆一样管理这部分内存,省去专门为方法区编写内存管理的代码 jdk6以前,然后发现在实现过程中这样的问题,

除了和Java堆一样不需要连续的内存和可以选 择固定大小或者可扩展外,甚至还可以选择不实现垃圾收集。

2.2.6 运行时常量池

运行时常量池是方法区的一部分。class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译器生成的各种字段和字符引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

2.2.7 直接内存
直接内存( Direct Memory )并不是虚拟机运行时数据区的一部分,也不是《 Java 虚拟机规范》中
定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现

2.3 HotSpot虚拟机对象探秘

2.3.1 对象的创建

首先在new的指令时,会检查类信息,既类是否被加载 解析 和初始化过,  在类加载确定后,将对象从堆内存中划分,有两种  如果堆内存是规整的,分配内存就仅仅把那个指针 往后挪一段与对象大小相等的距离, 指针碰撞;如果堆内存不规整,则 去寻找与需要对象内存大小足够大的空间,这种方式叫空闲列表。

对象在虚拟机中创建是非常频繁的行为,即仅仅是修改一个指针所指向的行为,有可能导致 a对象还没创建成功,然后b对象继续进行创建,内存分配出现问题。一种方案 是对分配内存空间动作进行同步处理-实际上虚拟机是采用cas配上重试机制保证原子型。

分配完内存 对象创建才刚刚开始,构造函数 

2.3.2 对象的内存布局

在new对象时,会创建对象的添加必要的设置

对象在堆内存中有三个部分  对象头,示例数据、和对齐填充 

2-1 HotSpot 虚拟机对象头 Mark Word

2.3.3 对象的访问定位

主流的访问方式主要有句柄和直接指针两种从虚拟机栈上访问堆上的对象数据

2-2 通过句柄访问对象

2-3 通过直接指针访问对象
句柄方式 使用代理 访问速度慢一点,但是不用频繁修改指针;而直接指针方式是访问快,对象移动时,改变移动指针
HotSpot 而言,它主要使用第二种方式进行对象访问

2.4 实战:OutOfMemoryError异常

2.4.1 Java堆溢出
在eclipse中设置 -xms20m -xmx20m -xx:+heapdumponoutofmemoryerror 
不断添加创建对象,并添加到数组中,使得gc roots 还在连接中,并抛出outofmemory 
常规的处理方法是首先通过内存映像分析工具(如 Eclipse Memory
Analyzer )对 Dump出来的堆转储快照进行分析 内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)
2.4.2 虚拟机栈和本地方法栈溢出
HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,栈容量只能由 -Xss 参数来设定
1 )如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError 异常。