JVM 的组成部分和作用
- 类加载系统:根据给定的全限定名加载object
- 执行引擎:执行class中的指令
- 本地方法栈:nativa方法
- 方法区:运行时数据区域
- 作用:首先通过编译器将java代码转换成字节码,然后类加载器将字节码加载到方法区,然后将指令交给执行引擎执行,执行引擎将字节码翻译成底层计算机指令(此时需要本地方法),再由CPU执行
JVM 内存模型
- 线程私有的
- 本地方法栈:native 方法
- 线程栈
- 栈帧:每个方法分配的内存空间
- 方法出口:方法结束时,执行哪个代码
- 局部变量表:局部变量
- 操作数栈:局部变量操作的数据
- 动态链接:引用类型指向内存区域的指针
- 栈帧:每个方法分配的内存空间
- 程序计数器:上下文切换时,记录程序运行到哪儿
- 线程共有的
- 元空间:类信息,常类,静态变量
- 堆:对象
Java 对象的创建过程
- 类加载检查:检查类是否加载到内存中,如果没有,进行类加载
- 分配内存空间
- 内存赋0值:内存空间全部设置为 0(除了对象头)
- 设置对象头:设置对象属性
- 初始化:调用初始化方法
类加载的过程
- 加载:根据全限定名加载class文件到内存中
- 连接
- 验证
- 准备
- 分配内存空间
- 变量赋初值
- 解析:常量池的符号引用解析成直接引用
- 初始化
如何判断对象是否死亡
- 引用计数法:每次对象被引用就+1,引用失效-1,引用为0,则死亡;
- 优点:简单,效率高
- 缺点:无法解决循环引用问题
- 可达性分析:由对象出发,根据引用追溯,不能追溯到jvm定义的 GC root 对象,则视为死亡;
GC root对象有哪些(java中不能变的)
- 栈帧中局部变量
- 元空间的静态变量、常量
- native对象
- 同步锁持有的对象
GC 垃圾收集有哪些算法,都有什么特点
- 标记-清除:将垃圾标记,然后等到安全点直接删除
- 缺点:会出现碎片化内存
- 标记-复制:将内存区域分为两块,每次使用一块区域,标记垃圾,等到安全点将非垃圾对象复制到另一块区域,格式化原区域
- 总有一半内存不使用
- 标记-整理:同标记清除类似,只不过将非垃圾对象向一端移动,清除其他区域
- 相对于上面两种,效率比较低
- 分代收集算法:将java堆分为新生代-老年代,在新生代对象朝生夕死,所以采用标记-复制,老年代对象相对稳定,所以采用标记-清除/标记-整理
什么是空间担保分配机制
是一种减少full GC的策略,在发生minor GC时,会检查老年代的内存空间是否大于当前新生代的总和。如果大于直接进行minor GC;如果小于,就会检查是否设置了空间担保,如果担保就会检查老年代内存空间是否大于历届minor GC 剩余的对象,如果大于,则尝试一次minor GC,否则直接 FULL GC
JVM内存溢出排查 OOM
- 通过 jps 查看服务 PID
- jmap -heap PID 查看内存情况
- jmap -dump:format=b,file=/tmp/heap.hprof PID 导出 dump 文件
- 然后可以通过本地分析工具进行分析(idea 插件 JProfiler)
怎么查看GC状态
- 通过 jps 查看服务 PID
- jstat -gc PID 事件间隔 打印次数
new Obect()占多少字节?
16字节!
一个对象包含三块区域:对象头(mark word、对象指针、数组长度)、实例数据、对齐填充;
- 对象头:
- mark word:8字节;
- 对象指针:4字节;
- 数组长度:单纯的对象不是数组,没有这块;
- 实例数据:new object() 没有实例数据,为空;
- 对齐填充:对象大小要求是 8 的整数倍,所以 还需要填充 4 字节