JVM
2020-07-02 10:01:14 0 举报
AI智能生成
JVM知识图谱
作者其他创作
大纲/内容
JVM基本术语
字面量
符号引用
直接引用
Java内存区域
运行时数据区域
线程隔离
程序计数器
虚拟机栈
本地方法栈
线程共享
堆
方法区
运行时常量池
直接内存
对象的创建过程
JVM遇到一条字节码new指令。
首先检查new指令参数是否能在常量池中定义到一个类的符号引用,并且检查这个符号引用代表的类是否被加载、解析、初始化。如果没有,那必须先执行相应的类加载过程。
为新生对象分配内存。
指针碰撞
空闲列表
并发问题:对象创建在虚拟机中是非常频繁的行
为,即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
对分配内存空间的动作进行同步处理,即采用CAS+失败重试的方式保证更新操作的原子性。
把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存, 称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
将分配的内存空间(不包含对象头)初始化为零值。这也保证了在 Java 代码中可以不赋初始值就直接使用。
设置对象头
执行Class文件的<init>()方法
对象的内存布局
对象头
用于存储对象自身的运行时数据(Mark Word)
类型指针(对象指向它的类型元数据的指针)
Java虚拟机通过这个指针来确定该对象是哪个类的实例
如果对象是一个Java数组, 那在对象头中还必须有一块用于记录数组长度的数据
实例数据
对象真正存储的有效信息。我们在程序代码里面所定义的各种类型的字段内容, 无论是从父类继承下来的, 还是在子类中定义的字段都必须记录起来。
对齐填充
起着占位符的作用。由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。 对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。
对象的访问定位
句柄
Java堆中将可能会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址, 而句柄中包含了对象实例数据与类型数据各自具体的地址信息。
好处:reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
直接指针(HotSpot)
Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销。
好处:速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本。
垃圾回收的三大问题
哪些内存需要回收?
什么时候回收?
如何回收?
对象存活判定
引用计数算法
可达性分析算法
GCRoots
虚拟机栈中引用的对象,比如各个线程被调用的方法栈中使用到的参数、局部变量、临时变量。
方法区中类的静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中 JNI 引用的对象。
Java 虚拟机内部的引用,比如基本数据类型对应的 Class 对象、系统类加载器。
所有被同步锁 synchronized 持有的对象。
反映Java虚拟机内部情况的JMXBean、 JVMTI中注册的回调、 本地代码缓存等。
引用
强引用
软引用
内存不够时,一定会被GC,长期不用也会被GC
弱引用
一定会被GC
虚引用
对象标记
第一次标记
对象在进行可达性分析后发现没有与 GCRoots 相连接的引用链
第二次标记
观察 F-Queue 中的对象是否有与 GCRoots 相连接的引用链
方法区回收
废弃常量
没有对象使用的常量
无用的类
该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
加载该类的 ClassLoader 已经被回收。
该类对应的 Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
垃圾回收算法
分代收集理论
弱分代假说
绝大多数对象都是朝生夕灭的
强分代假说
熬过越多次垃圾收集过程的对象就越难以消亡
跨代引用假说
存在互相引用关系的两个对象, 是应该倾向于同时生存或者同时消亡的。举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了。
相关术语
部分收集(Partial GC)
新生代收集(Minor GC/Young GC)
老年代收集(Major GC/Old GC)
混合收集(Mixed GC)
整堆收集(Full GC)
标记-清除算法
存在空间碎片化问题
需要扫描所有对象,效率低
标记-复制算法
Eden和Survivor
只需要扫描存活的对象,效率更高
不会产生碎片
适合生命周期比较短的对象(年轻代)
空间浪费,需要分配担保机制
标记-整理算法
不会产生碎片
比标记-清除算法耗费更多的时间进行压缩
适合老年代使用
分代收集算法
年轻代使用标记-复制算法
老年代使用标记-整理(清除)算法
GC的时机
Minor GC
新对象生成时,Eden空间满了
Full GC
老年代满了
永久代(元空间)满了
调用system.gc()
垃圾收集器
并行与并发
并行:指多个收集器的线程同时工作,但是用户线程处于等待状态。
并发:指收集器在工作的同时,允许用户线程工作。
并发不代表解决了GC停顿的问题,在关键的步骤还是要停顿。比如在收集器标记垃圾的时候。但在清除垃圾的时候,用户线程可以和GC线程并发执行。
性能指标
内存占用
吞吐量
延迟
经典垃圾收集器
年轻代收集器
Serial 收集器
ParNew 收集器
Parallel Scavenge 收集器
老年代收集器
Serial Old 收集器
Parallel Old 收集器
CMS 收集器
年轻代+老年代收集器
Garbage First 收集器
组合搭配
Serial + Serial Old
Serial + CMS
ParNew + Serial Old
ParNew + CMS
Parallel Scavenge + Serial Old
Parallel Scavenge + Parallel Old
G1
低延迟垃圾收集器
Shenandoah 收集器
ZGC 收集器
JVM 调优常用参数
-Xms20M
堆的初始化容量
-Xmx20M
堆的最大容量
-Xmn10M
堆中新生代的容量
-XX:+PrintGCDetails
打印出GC信息
-XX:SurvivorRatio=8
Survivor区的比例,80%
标记-清除算法
标记-整理算法
标记-复制算法
设计原则
0 条评论
下一页