本文是对《深入拆解 Java 虚拟机》的学习总结,很不错的课程,推荐学习。
01 | Java代码是怎么运行的?
-
虚拟机角度看
以标准 JDK 中的 HotSpot 虚拟机为例,Java 代码首先被编译成class文件加载到 Java 虚拟机中。加载后的 Java 类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
JVM内存模型解释 -
硬件角度看
Java 字节码无法直接执行,Java 虚拟机需要将字节码翻译成机器码。翻译过程有两种形式:
1、解释执行:逐条将字节码翻译成机器码并执行;
2、即时编译(Just-In-Time compilation,JIT):将一个方法中包含的所有字节码编译成机器码后再执行。
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
类加载步骤:加载、链接(验证、准备、解析)、初始化三个步骤。
-
加载
查找字节流,并且据此创建类的过程,
双亲委派模型。每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。 -
链接
将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。它可分为验证、准备以及解析三个阶段。
验证阶段的目的,在于确保被加载类能够满足 Java 虚拟机的约束条件。
准备阶段的目的,则是为被加载类的静态字段分配内存。
解析阶段的目的,正是将这些符号引用解析成为实际引用。如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载(但未必触发这个类的链接以及初始化。) -
初始化
便是为标记为常量值的字段赋值,以及执行 < clinit > 方法的过程。Java 虚拟机会通过加锁来确保类的 < clinit > 方法仅被执行一次。
04 | JVM是如何执行方法调用的?(上)
-
重载与重写
1、重载
定义:同一个类中,方法名相同,但参数个数或类型不同。
调用:重载的方法在编译过程中即可完成识别。Java 编译器会根据所传入参数的声明类型(注意与实际类型区分)来选取重载方法。
2、重写
定义:子类与父类中有相同的方法名、请求参数、返回类型。
调用:会根据调用者的动态类型,来选取实际的目标方法。 -
JVM 的静态绑定和动态绑定
11 | 垃圾回收(上)
-
引用计数法
为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。一旦某个对象的引用计数器为 0,则说明该对象已经死亡,便可以被回收了。
缺点:无法处理循环引用对象。 -
与可达性分析
将一系列 GC Roots 作为初始的存活对象合集(live set),然后从该合集出发,探索所有能够被该集合引用到的对象,并将其加入到该集合中,这个过程我们也称之为标记(mark)。最终,未被探索到的对象便是死亡的,是可以回收的。
缺点:在多线程环境下,其他线程可能会更新已经访问过的对象中的引用,从而造成误报(将引用设置为 null)或者漏报(将引用设置为未被访问过的对象)。 -
Stop-the-world 以及安全点
Stop-the-world:停止其他非垃圾回收线程的工作,直到完成垃圾回收。
Java 虚拟机中的 Stop-the-world 是通过安全点(safepoint)机制来实现的。当 Java 虚拟机收到 Stop-the-world 请求,它便会等待所有的线程都到达安全点,才允许请求 Stop-the-world 的线程进行独占的工作。 -
垃圾回收的三种方式
1、清除(sweep),即把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)之中。当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。
缺点:会造成内存碎片;分配效率较低。
2、压缩(compact),即把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。这种做法能够解决内存碎片化的问题,但代价是压缩算法的性能开销。
3、复制(copy),即把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。复制这种回收方式同样能够解决内存碎片化的问题,但是它的缺点也极其明显,即堆空间的使用效率极其低下。
12 | 垃圾回收(下)
- Java 虚拟机的堆划分
Java 虚拟机将堆划分为新生代和老年代。其中,新生代又被划分为 Eden 区,以及两个大小相同的 Survivor 区。
Minor GC:Eden 区满了,Eden 区和 from 指向的 Survivor 区中的存活对象会被复制到 to 指向的 Survivor 区中,然后交换 from 和 to 指针。该过程采用了标记 - 复制算法。
如果一个对象被复制的次数为 15(对应虚拟机参数 -XX:+MaxTenuringThreshold),那么该对象将被晋升(promote)至老年代。另外,如果单个 Survivor 区已经被占用了 50%(对应虚拟机参数 -XX:TargetSurvivorRatio),那么较高复制次数的对象也会被晋升至老年代。采用标记 - 复制算法。
13 | Java内存模型
-
happens-before 关系
是用来描述两个操作的内存可见性,具备传递性。 -
Java 内存模型的底层实现
Java 内存模型是通过内存屏障(memory barrier)来禁止重排序的。
https://zhanghan.blog.csdn.net/article/details/109255980