Java代码的执行过程
首先在执行前把Java代码编译成字节码(ByteCode)。然后由JVM执行字节码。
字节码执行涉及字节码的解释、编译,以及解释/编译代码的执行。

Java程序在执行过程中即存在解释执行也存在编译执行,还存在编译优化(Just-In-Time)。
Java执行架构图

C1编译器速度快,比那一质量低。
C2编译器时间长,但是质量高。
C++面向对象的支持:
- 封装支持:通过把this放在参数列表中,将成员函数转化成C语言的普通函数。
- 继承支持:编译器把父类的成员变量全部复制到子类中实现继承。
- 多态支持:
- 静态多态:对函数名进行编码(name mangling)生成唯一的一个函数名。
- 动态多态:函数重写(override),通过虚函数实现。生成一个虚函数表,存放编译后的函数地址。
GC算法分类:
引用计数(reference count)法和可达性分析(tracing)法(也称为根引用分析法)。
引用计数法实现方便,但是存在一些问题:
- 并发场景中,为了使计数器的修改同步,需要使用很复杂的无锁算法。
- 引用计数在对象回收时会引发链式反应,所以回收时时间不可控。
- 无法有效的解决循环引用的问题。
JVM使用的是可达性分析法。
GC对象的表示方式:
在移动式的GC算法中,为了保证对象在GC过程中只移动一次,通常需要记录对象移动前后的映射关系,记录信息的方式有多种。
早期的GC实现中倾向于记录在对象头中,而最新的垃圾回收实现因为算法的复杂性,可能需要借助额外的数据结构来保证GC的正确性。
promoted的状态,promo_bits通过重用加锁位(11)来保存对象头。【这块没看懂】
GC算法概述
GC算法到目前为止基本没有太大的创新,可以分为复制算法(Copying GC)、标记清除(Mark-Sweep GC)和标记压缩(Mark-Compact GC)。
复制算法
复制算法空间利用率有限,但是效率很高,而且没有碎片化问题。
将空间分成两份,From Space和 To Space,From用于应用的内存分配,To用于执行GC时活跃对象的转移,GC执行时会将所有活跃对象转移到To中。GC完成后From和To交换。
这个算法有个缺点,内存利用率只有50%。JVM对复制算法进行了优化,分成3个部分:Eden、Survivor0、Survivor1。通常比例为8:1:1,这个分配的依据是,大部份的对象都会很快死亡。
标记清除算法
标记清除算法空间利用率高,但是要考虑内存分配和碎片化的问题。
标记清除算法使用链表的方式来管理内存。在GC时先遍历所有活跃对象,然后再将非活跃对象释放并加入空闲链表,这就需要额外的控制策略,将会在之后的章节中展示更多细节。
标记压缩算法
标记压缩算法用来解决标记清除算法的碎片化问题,一般作为保底方法。
分代回收
按对象的生命周期进行划分新生代和老生代,新生代对象生命周期短使用复制算法进行管理,老生代使用标记清除算法管理。
新老生代的周期管理有弱分代理论假设和强分代理论假设两种:
- 弱分代理论假设:对象分配后内存很快使用,并且使用后很快就不再使用(内存可以释放)。
- 强分代理论假设:假定对象长期存活,未来此类对象还长期存活。
目前弱分代理论在高级语言中普遍得到证实和认可。强弱分代理论在JVM中均有体现。
固定和浮动边界:浮动边界对使用者友好,但是实现难度增加很多,只有一款垃圾回收器实现了边界浮动,并在JDK15中移除。
GC的根
JVM中存在两种类型的根:强根/弱根。强根时真正的根,标记活跃对象。弱根只是为了支持语言特性。
强根
会进行精确GC,对此会额外消耗15%-40%的性能,但是一旦缺少了一个根会导致遗漏一些活动对象。
弱根
- 软引用:对象只有满足一些条件才会进行回收。
- 弱引用:在垃圾回收时发现内存不足时回收。
- 虚引用:需要一个引用队列,对象被执行后可以被GC回收。
- Finalize:如果Java类重载了Finalize()函数,则需要通过Finalize线程处理。
软引用/弱引用:GC时需要额外标记,在强根遍历结束后再根据策略处理。
虚引用/Finalize引用:GC时也需要额外记录,当GC结束后,引用对象可以在下一次GC执行过程中回收。
JVM中根的构成
- 类元数据对象
- Java对象
- Universe,Java程序运行时需要的一些全局对象
- Monitor,全局监视器对象
- JNI,JVM执行本地代码时使用API产生的对象
- JVMTI,使用JVM提供的接口用于调试、分析Java程序
- System Dictionary,JVM在设计类加载时,对于基本的类会单独加载,单独标记
- Management 是JVM提供的内存管理API
- AOT,在JDK9之后引入了提前编译
- 语言特性的弱引用
- JVM弱根,例如管理String中intern产生的对象
安全点
GC运行时,为了遍历对象的引用关系,需要暂停程序运行,防止程序修改对象的引用关系导致标记错误。暂停程序就是STW(Stop The World)。
STW涉及的第一个概念就是安全点(safepoint),让VMThread能够原子地运行,不受Mutator干扰;实现简单。
解释线程进入安全点
解释器执行完当前字节码然后暂停。
解释进程进入安全点时间可控,最大等待时间是一条字节码的时间。
编译线程进入安全点
需要添加额外代码让编译代码执行时可以主动暂停。指令多少的权衡,指令类型的权衡。
本地线程进入安全点
在本地线程进入Java时暂停。
JVM内部并发线程进入安全点
一种不访问堆,就不影响GC;如果访问堆,会主动挂起,等待GC结束唤醒。