自动内存管理机制
Java内存区域与内存溢出异常
运行时数据区域
- 程序计数器
当前想蹭所执行的字节码的行号指示器
- Java虚拟机栈
每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
- 本地方法栈
和虚拟机栈类似,区别为本地方法栈为Native方法服务
- Java堆
时Java虚拟机所管理的内存中最大的一块,存放对象实例,是垃圾收集器管理的主要区域
- 方法区
各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 运行时常量池
是方法区的一部分
- 直接内存
用于NIO
虚拟机内的对象
内存布局
对象头:
Mark Word,存储对象自身的运行时数据,如哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
类型指针,确定这个对象是哪个类的实例实例数据
对象真正存储的有效信息对齐填充
- 访问定位
句柄,Java堆中划分出句柄池,引用存储句柄的地址
直接指针
- 垃圾收集器和内存管理策略
堆和方法区
对象已死吗?
- 引用计数算法
- 可达性分析算法
- 引用:
强引用、软引用、弱引用、虚引用
- 回收方法区
废弃常量和无用的类
垃圾回收算法
- 标记-清除算法
- 复制算法
- 标记整理算法
- 分代回收算法
垃圾收集器
- CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短停顿时间为目标的收集器,CMS收集器采用 标记–清 除 算法,运行在老年代。主要包含以下几个步骤:初始标记, 并发标记, 重新标记, 并发清除
其中初始标记和重新标记仍然需要“Stop the world”。初始标记仅仅标记GC Root能直接关联的对象,并发标记就是 进行GC Root Tracing过程,而重新标记则是为了修正并发标记期间,因用户程序继续运行而导致标记变动的那部 分对象的标记记录。
由于整个过程中最耗时的并发标记和并发清除,收集线程和用户线程一起工作,所以总体上来说,CMS收集器回 收过程是与用户线程并发执行的。
虽然CMS优点是并发收集、低停顿,很大程度上已经是一个不错的垃圾收集 器,但是还是有三个显著的缺点:CMS收集器对CPU资源很敏感。在并发阶段,虽然它不会导致用户线程停顿,但是会因为占用一部分线程 (CPU资源)而导致应用程序变慢。
CMS收集器不能处理浮动垃圾。所谓的“浮动垃圾”,就是在并发标记阶段,由于用户程序在运行,那么自然就 会有新的垃圾产生,这部分垃圾被标记过后,CMS无法在当次集中处理它们,只好在下一次GC的时候处理, 这部分未处理的垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段程序还需要运行,即还需要预留足够的内存
空间供用户使用,因此CMS收集器不能像其他收集器那样等到老年代几乎填满才进行收集,需要预留一部分 空间提供并发收集时程序运作使用。要是CMS预留的内存空间不能满足程序的要求,这是JVM就会启动预备 方案:临时启动Serial Old收集器来收集老年代,这样停顿的时间就会很长。由于CMS使用标记–清除算法,所以在收集之后会产生大量内存碎片。当内存碎片过多时,将会给分配大对 象带来困难,这是就会进行Full GC
- G1
G1收集器与CMS相比有很大的改进:
G1收集器采用标记–整理算法实现, 可以非常精确地控制停顿。G1收集器可以实现在基本不牺牲吞吐量的情况下完成低停顿的内存回收,这是由于它极力的避免全区域的回 收,G1收集器将Java堆(包括新生代和老年代)划分为多个区域(Region),并在后台维护一个优先列表,每 次根据允许的时间,优先回收垃圾最多的区域 。
内存分配和回收策略
- 对象优先在Eden分配
- 大对象直接进入老年代
- 长期存活的对象进入老年代
- 动态对象年龄判定
- 空间分配担保
虚拟机性能监控和故障处理工具
虚拟机执行子系统
- 类文件结构
字节码是构成平台无关性的基石
字节码指令简介
虚拟机类加载机制
类加载的时机
加载、验证、准备、解析、初始化、使用、卸载
- 加载时机:对一个类进行主动引用的场景中
类加载的过程
加载
验证
准备
解析
初始化
类加载器
双亲委派机制
启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器虚拟机字节码执行引擎
运行时栈帧结构
每一个栈帧包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息
方法调用
解析
分派
Java虚拟机中有四种方法调用指令:
invokestatic :调用静态方法。
invokespecial :调用实例构造器方法,私有方法和super。
invokeinterface :调用接口方法。
invokevirtual :调用以上指令不能调用的方法(虚方法)。在Java中,符合“编译时可知,运行时不可变”这个要求的方法主要是静态方法和私有方法。这两种方法都不能通过 继承或别的方法重写,因此它们适合在类加载时进行解析 只要能被 invokestatic 和 invokespecial 指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合这个条件的 有:静态方法、私有方法、实例构造器、父类方法,他们在类加载的时候就会把符号引用解析为改方法的直接引 用。这些方法被称为非虚方法,反之其他方法称为虚方法(final方法除外)。
静态分派
适合于方法重载
静态分派主要针对重载,方法调用时如何选择。在上面的代码中, People 被称为变量的引用类型, Man 被称为变 量的实际类型。静态类型是在编译时可知的,而动态类型是在运行时可知的,编译器不能知道一个变量的实际类 型是什么。编译器在重载时候通过参数的静态类型而不是实际类型作为判断依据。并且静态类型在编译时是可知的,所以编 译器根据重载的参数的静态类型进行方法选择。
动态分配
适合方法重写单分派和多分派
基于栈的字节码解释执行引擎
解释执行
基于栈的指令集和基于寄存器的指令集
程序编译和代码优化
早期优化(编译期)
javac
- 语法糖
晚期优化(运行期)
JIT,即时编译器
- 解释器
- 高效并发
Java学习笔记(三)JVM
最新推荐文章于 2023-04-24 11:12:40 发布