jvm内存结构
方法区
对象分配
类加载过程
加载:
- 读取类的二进制流
- 转为方法区数据结构,并存放到方法区
- 在Java堆中产生java.lang.Class对象
验证:
- 作用: 验证class文件是不是符合规范
- 文件格式的验证(是否以0xCAFEBABE开头 、 版本号是否合理)
- 元数据验证(是否有父类、是否继承了final类、非抽象类实现了所有的抽象方法)
- 字节码验证:(运行检查、栈数据类型和操作吗操作参数吻合、跳转指令指向合理的位置)
- 符号引用验证:(常量池中描述类是否存在、访问的方法或字段是否存在且有足够的权限)
准备:
- 作用:为类的静态变量分配内存, 初始化为系统的初始值
- fianl static 修饰的变量:直接复制为用户定义的值,比如private final static in value = 123 , 直接赋值123
- private static int value = 123 , 该阶段的值依然是0
解析:
- 作用:符号引用转换成直接引用
字节码是如何运行的?
- 解释执行:有解释器一行一行翻译执行
- 编译执行:把字节码编译成机器码,直接执行机器码
解释 vs 编译
解释执行:
- 优势在于没有编译的等待时间
- 性能相对差一些
编译执行:
- 运行效率会高很多, 一般认为比解释执行快一个数量级
- 带来了额外的开销 (内存开销、cpu开销)
如何找到热点代码
-
基于采样的热点探测 (周期的检测各个线程的栈底是不是会出现同一个方法)
-
基于计数器的热点探测 (为每个方法或者是代码块设置计数器 , 统计计数器的值是否达到阈值)
-
Hotsort采用的是基于计数器的热点探测
热点方法的内联
- 把目标方法的代码复制到发起调用的方法之中,避免发生真实的方法调用
方法内联的条件一:
- 方法体足够小
方法内联的条件二:
- 被调用方法运行时的实现被可以唯一确定
- static、private方法以及final方法,JIT可以唯一确定具体的实现代码
- public的实例方法,指向的实现可能是自身、父类、子类的代码,当且仅当JIT能够唯一确定方法的具体实现时 , 才有可能完成内联。
方法内联的注意点:
- 尽量让方法体小一些
- 尽量使用final、private、static关键字修饰方法,避免因为多态,需要对方法做额外的检查
- 一些场景下,可通过JVM参数修改阈值,从而让更多方法内联。
垃圾收集器
Stop The World
- Java代码停止运行,native代码继续运行,但不能于JVM进行交互
- 多半是由于垃圾回收导致;也可能由Dump线程、死锁检查、Dump堆等导致
- 危害:服务停止、没有响应;主从切换、危害生产环境
Serial收集器
特点:
- 单线程
- 简单、高效
- 运行过程中工作现场全部停止
ParNew收集器
特点:
- 多线程
- 垃圾收集线程数可设置
Parallel Scavenge收集器 (吞吐量优先收集器)
特点:
- 可以达到一个可控制的吞吐量
- 自适应GC策略
Serial Old 收集器
Parallel Old 收集器
CMS 收集器 (并发标记清除)
主要是使用的复制算法,Region 之间的复制
- 初始标记
- 并发标记
- 并发预清理
- 并发可中止的预清理阶段
- 重新标记
- 并发清除
- 并发重置
G1收集器
设计思想:
- 内存分块
- 跟踪每个Region里面垃圾堆积的价值大小
- 构建一个优先列表, 根据允许的收集时间,优先回收价值高的Region
GC分类:
- Yung GC (所有的Eden Region都满了的时候,就会触发Yong GC)
- Mixed GC (老年代大小占整个堆的百分比达到一定阈值会触发Mixed GC , Mixed GC 会回收所有的Yong Region , 同时回收部分Old Region)
- Full GC 使用Serial Old发生
【初始标记、并发标记、最终标记、筛选回收】