《深入理解JVM》读书笔记
内存模型
程序计数器
- 当前线程所执行的字节码的行号指示器,线程私有。字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令。
java虚拟机栈
- 线程私有,生命周期与线程相同。
- Java方法执行的内存模型。每个方法创建一个栈帧,包含局部变量表,操作数栈等。
- 局部变量表存储编译期可知的基本数据类型,对象引用类型
本地方法栈
- 为native本地方法服务,与虚拟机栈类似。
java堆
- 所有线程共享
- 存放对象实例
- 垃圾收集重要区域,可以分为新生代和老年代,Eden、From Survivor和To Survivior
方法区
- 所有线程共享
- 存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 包含运行时常量池, class文件中存放的字面量和符号引用,类加载后进入运行时常量池存放。运行时常量池具备动态性,不止编译期产生的常量可以存放,运行期也可以存储。
- HotSpot使用永久代来实现方法区,垃圾回收主要针对常量池回收和类的卸载。JDK1.8后,采用元数据区实现方法区的作用。
对象创建
内存分配
- 指针碰撞
- 空闲列表
垃圾收集机制
概念
- Minor GC:指发生在新生代的垃圾收集动作,新生代Eden区满触发。
- Full GC:同时回收新生代、老年代,触发条件多:
- 调用System.gc时,系统建议执行Full GC,但是不必然执行
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
- Minor GC发生时分配担保失败
- Stop The World:分析期间执行系统对象引用关系保持固定。
- 安全点:程序执行时并非所有地方都能停顿下来开始GC,只有到达安全点时才可以暂停。
- 安全区域:在一段代码片段中,引用关系不会发生变化。安全区域任意地方开始GC都是安全的。
确定回收对象
- 引用计数法,简单高效,存在循环引用问题
- 可达性分析,选定根节点并向下搜索,当对象和根节点之间没有引用链时,则此对象不可达
根节点包括:- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
引用的概念
引用分为强引用、软引用、弱引用和虚引用,强度依次减弱。
- 强引用,代码中“Object obj = new Object()”这类引用,垃圾收集器不回收强引用对象。
- 软引用,系统将要内存溢出前会进行回收。
- 弱引用,关联对象会在下一次垃圾收集中回收。
- 虚引用,不影响对象生存周期,目的是对象回收时,能收到系统通知。
两次标记过程
- 第一次标记,对象可达性分析后,没有与根节点相连的引用链。
- 筛选一次标记对象是否有必要执行finalize()方法,对象没有覆盖finalize()或者已经执行过finalize(),则此对象不需要执行。
- 需要执行finalize()的对象置入F-Queue中,并执行方法,finalize()中对象要与引用链上的对象建立连接,这是对象拯救自己被回收的最后一次机会。
- 第二次标记,F-Queue中没有自救的对象会被二次标记并回收。
- 对象的finalize()只会被系统自动调用一次,只有一次自救的机会 。
垃圾回收算法
- 标记清除算法
- 复制算法
- 标记整理算法
- 分代收集算法
垃圾收集器
Serial:新生代收集器。单线程,工作时必须停止其它线程(Stop the world),采用复制算法。
ParNew:Serial的多线程版本,同样需要暂停用户所有进程。
Parallel Scavenge:新生代收集器,采用复制算法,并行多线程。特点是可控制吞吐量,有GC自适应调节策略,是与ParNew的重要区别。
Serial Old:Serial的老年代版本
Parallel Old:Parallel Scavenge的老年代版本,多线程和标记整理算法
CMS:老年代收集器,以获取最短回收停顿时间为目标,基于标记清除算法。四个阶段,初始标记和重新标记要“Stop the world”:初始标记、并发标记、重新标记、并发清除。耗时最长的并发标记和并发清除过程可以与用户一起工作。
缺点:1.对CPU资源敏感。 2. 无法处理浮动垃圾 3. 基于标记清除算法
G1:基于标记整理算法,并行与并发,分代收集,空间整理,可预测的停顿。堆内存被划分为多个大小相等的内存块(Region)。使用Remembered Set来避免全堆扫描。初始标记、并发标记、最终标记、筛选回收。