导图社区 JVM
JVM核心知识总结,涵盖面试的绝大多数的考点。
编辑于2020-05-27 20:53:48JVM
方法调用
解析调用
解析调用是指编译期就可以完成符号引用到直接引用的转换,就可以确定调用的方法的版本,就可以完成方法的调用
分派
静态分派(与重载密切相关)
依赖静态类型来决定方法执行的版本,发生在编译阶段
动态分派(与重写密切相关)
实现方法是为每个类在方法区建立虚方法表,虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那么子类的虚方法表中的地址入口与父类相同的方法一致,都指向父类的实现入口。如果子类重写了这个发发你,子类虚方法表中的地址也会被替换为指向子类实现版本的入口地址。
范型
原理
Java的范型采用的是类型擦除式范型,也就是说代码编译之后不存在范型信息了。范型最终都是会被转换为Object的。
缺点:
涉及到频繁的拆箱装箱过程,效率很低。
不支持运行时获取范型的数据类型,语法啰嗦
对重载有一定的影响(Java的范型是全面落后其它语言的)
JMM
主内存与工作内存
volatile
可见性
对volatile变量的写操作会强制刷新到主存,并使其它处理器核心的缓存失效。以此来保证可见性
有序x性
volatile修饰的变量不会参与指令重排
先行发生原则
内存屏障
Load Barrier
在读指令之前插入读屏障,可以使缓存失效。迫使从主存中读取数据
Store Barrier
在写入命令之前插入写屏障,可以保证写入到缓存的数据刷新到主存
实际使用
LoadLoad
StoreStore
LoadStore
StoreLoad
volatile变量的写操作涉及该屏障
JAVA线程
HotSpot虚拟机线程的实现
每个Java线程直接映射到一个操作系统原生线程来实现,中间没有任何间接结构。且HotSpot虚拟机不会干涉线程调度。
状态
新建
无限期等待
没有设置时间的wait,join,park等
限期等待
设置了时间的sleep,wait,join,park等
阻塞
等待锁
结束
线程的实现
使用内核线程实现
使用用户线程实现
使用用户线程加轻量级进程混合实现
对象的创建
类加载
空间分配
对对象进行必要的设置
执行<init>,即执行构造函数
锁优化
自旋锁与自适应循环
锁消除
虚拟机即时编译器在运行是,对一些代码要求同步,但是对检查到不可能存在共享数据竞争的锁进行消除
锁粗化
锁的粒度比较小的时候可能会出现频繁的加锁和解锁。这个时候就可以对锁的作用访问进行拓展合并几个同步区域,以提高效率。
偏向锁
偏向锁的核心就是正在无竞争的情况夏将这个同步取消掉。当锁被第一次获取时,会记录偏向线程ID和锁状态,持有偏向锁的线程下次进入无需加锁。当另一个线程尝试区获取这个锁的时候,偏向锁将会升级为轻量级锁。
轻量级锁
轻量级锁是在无竞争的时候使用CAS来替换互斥量来提高效率。
运行时数据区
线程私有
程序计数器
虚拟机栈
本地方法栈
线程间共享
堆区
线程共享的堆中依然可以划分出线程私有的空间,比如TLAB(线程分配缓存区)以提高对象分配时的效率。
方法区
方法区目前已经采用本地内存来实现了
线程安全
定义
当多个线程同时访问一个对象时,如果不用考虑这些线程运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用时方法进行任何的协调,调用这个对象的行为都可以得到正确的结果,那就称这个对象是线程安全的。
实现途径
不可变
绝对线程安全
相对线程安全
线程对立
线程兼容
对象的访问定位
通过句柄访问
堆中将划分一部分空间作为句柄池,refrence存放的是句柄地址,而句柄中存放了直接地址。句柄定位方式有助于对象移动过程的处理。
通过直接地址访问定位
refrence存放的是对象的直接地址,使用直接指针的方式来定位对象,效率更高,因为少了一次指针定位的时间开销。
JDK8新特性
接口默认方法
lambda表达式
函数式接口
Stream流
全新的Date API
多重注解
常用工具
jps:虚拟机进程状态工具
jstat:虚拟机统计信息监视工具
jstat -gcutil 2764 ;可查看各分代大小,GC次数,GC总时间等信息,可用来排除频繁GC导致的卡顿。
jinfo:Java配置信息工具
jmap:Java内存映像工具
jhat:虚拟机堆转储快照分析
jstack:Java堆栈跟踪工具
对象的内存布局
对象头
哈希码
GC分代年龄
锁状态标记
线程持有的锁
偏向时间戳
实例数据
存储我们在程序代码中定义的各种字段,包括从父类继承的
对齐填充
没有特别的的要求,但是hotspot虚拟机要求对象的起始地址必须为8字节的整数倍。
对象存活判断
引用计数法
原理
为每一个对象添加一个引用计数,每当有一个地方引用了它引用计数器的值加一。当引用失效时,计数器的值减一。当计数器的值为0时,认为对象可以被回收了。
缺陷
无法解决对象之间的循环引用问题
可达性分析法
原理
从GCRoot开始向下搜索,如果对象不可达,那么认定该对象可以被回收
GCRoot
虚拟机栈中引用的对象
本地方法栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
虚拟机内部的引用
所有被同步锁持有的对象
反映Java虚拟机内部情况的JMXBean等对象
引用
强引用
软引用
当内存不足时被回收,可以使用SoftReference来实现
弱引用
下一次GC时会被回收,可以使用WeakRefrence来实现
虚引用
在回收时可以收到一个通知,用PhantomReference实现
垃圾收集算法
标记清除算法
首先标记需要被回收的对象,标记完成后统一回收掉标记的对象。它的缺点时执行效率不问题,效率随着对象的增长而降低。并且该算法会产生大量的内存碎片。
标记整理算法
标记所有需要回收的对象,然后将存活的对象移动到一端,然后清除边界之外的内存。
复制算法
将内存按容量分为两个部分,每次只使用其中一部分。垃圾回收时,将存活的对象复制到另一部分,然后清除使用过的一一半内存。
分代收集理论
弱分代学说
绝大多数对象都是朝生夕灭的
强分代学说
熬过越多次垃圾收集的对象就越难灭亡
跨代引用假说
跨代引用相对于同代引用是少数
经典垃圾收集器
Serial
单线程新生代收集器。采用复制算法,会有STW。
ParNew
是Serial的多线程版本同样也是新生代收集器,采用复制算法会有STW。
Parallel Scavenge
基于标记复制算法的新生代收集器
Serial Old
Serial的老年代版本,采用的标记整理算法,同样也是单线程的。
Parallel Old
Parallel Scavenge的老年代多线程收集器,采用的是标记整理算法。
CMS
基于标记清除算法的收集器,一般用作老年代收集器。它的垃圾收集过程经历初始标记,并发标记,重新标记,并发清除的过程。其优点停顿时间短,缺点是无法清除浮动垃圾。
G1
G1收集器将整个堆空间划分为许多个Region,它不再重物理上区分新生代和老年代,而是根据每个分区的回收价值来进行回收。G1还提供了Humongous区域来专门用来存储大对象。
内存分配与回收策略
对象优先在Eden区分配
大对象直接进入老年代
长期存活的对象进入老年代
空间分配担保
当回收Eden区时候,需要确保Servivor区能够有足够的空间存放存活的对象,为了避免YGC后Servivor空间不足,因此需要老年代进行空间分配担保,当Servivor区空间不足以容纳存活的对象时,将利用老年代的空间。这就是空间分配担保机制。根据统计信息,当老年代的空间也不足以多于的对象时,那么就会空间分配担保失败,这个时候就会触发Full GC。
类加载过程
加载
通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行是数据结构
在内存中生成一个代表这个类的Class对象。
验证
验证字节流是否复合Class文件的规范
对字节码描述的信息进行语义分析
验证程序语义是否合法、和是否符合逻辑
准备
为类中定义(静态变量)的变量分配内存并设置类变量初始值
解析
Java虚拟机将常量池内的符号引用替换为直接引用的国产
初始化
执行<clinit>方法,完成变量的初始化
类加载器
作用
实现通过类的全限定名加载描述该类的二进制流信息
分类
启动类加载器:Bootstrap Class Loader
扩展类加载器:Extension Class Loader
应用程序类加载器:Application Class Loader
自定义类加载器
双亲委派机制
如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有加载请求都会被传送到父类加载器的启动类加载器中,只有当父加载器反馈自己无法完成加载后,子类加载器才会尝试进行类加载。
双亲委派机制确保了沙箱安全
破环双亲委派机制
在ClassLoader抽象类中添加findClass
引入线程上下文加载器
为了实现模块热部署机制
栈帧结构
局部变量表
是一组变量值的存储空间,存储方法参数和方法内部定义的局部变量
操作数栈
是一个先进后出的栈,用于在方法运行时各种字节码指令往操作数栈中写入和提取内容。
动态链接
每个栈帧中都包含了一个指向运行时常量池中该栈帧所属方法的引用。符号引用在运行时转换为直接引用的过程就被称为动态链接。
方法返回地址