JVM面试资料
JDK JRE JVM
JDK:api开发
JRE:运行环境
JVM 和操作系统虚拟机
类加载机制是什么?

类加载机制 包含 装载(loading ) 链接(linking) 初始化(lnitalizing)
一 装载 (loading )
1 将ClassFile--》字节流---》类加载器classLoader加载该字节流
2 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(放到方法区后如何找到?) 将class文件(类信息 模板数据 符号引用)放进 运行时数据区
3 在我们堆中生成一个代表这个类的java.lang.class对象 作为方法区中数据的访问入口
二 链接(linking) 分为
1 验证verification 判断是否是class文件 符号引用验证 元数据验证 字节码验证
2 准备prepareation 为类的静态变量分配内存 并赋值 当前类型的默认值
private static int a=1; 它在我们得准备阶段 值为0
3 解析resolution 解析是从运行时常量池中的符号引用动态确定具体值得过程---》符号引用转换为直接引用
符号引用:字面上是引用 但是并没有真正指向内存地址

直接引用:符号引用在jvm中生效了 指向了内存开辟的地址
初始化(lnitalizing):方法执行到Clinit阶段 初始化静态变量的值 初始化静态代码块 初始化当前类的父类
类加载器ClassLoader 有哪些?

双亲委派
运行时数据区(Run-Time Data Areas)
线程共享(数据每个线程拿的到) 方法区 堆
线程私有(只有当前线程能拿到数据) 程序计数器 本地方法栈 java虚拟机栈
对象的堆存储是由垃圾回收器回收 见GC
程序计数器:记录栈帧运行位置
本地方法栈
java虚拟机栈:线程私有 最小存储单元:栈帧
局部变量表:方法中的局部变量,方法的参数
操作数栈:也是一个栈 是以压栈和出栈的方式来存储操作数
int a=1; //
int b=1;
int c=a+b;
方法返回地址:
一个方法执行之后 只有两种情况可以退出
1 遇到返回的字节码指令
2
异常返回
1
2
动态链接:将符号方法引用转换为具体的方法引用(这里和resolution 类似 因为有可能方法中还调用了其他方法 而这样在resolution 阶段无法读取到,
只有到java虚拟机栈中才能 动态链接到其他内部方法)
堆 为什么进行分代 设计
方法区:非堆
可以直接用对来存储对象 但是每个对象存在声明周期 有的声明周期长 有的声明周期短 那么我们就可以将堆划分成两块
一块是old区 一块是young区 每当进行一次垃圾回收我们就可以将这个对象声明周期+1 当到达声明周期到达15之后,
就将该对象转移到old区 --------》为什么是15 因为对象的声明周期 是存储在markword中 他的取值范围是0000-1111
也就是说最大值15
为什么将young区分为s1和s2区?
当我们放入的对象太大(大小为2) 而young区无法放下(剩余大小为2 但是内存空间不连续,所以无法放下),
所以我们将Young区分为 Eden区 和s区 每次垃圾回收 都会将对象迁移到s区 这样Eden区就能有连续的空间来存放对象了
但是我们得s区无法容纳我们Eden区的对象 因此 可以将S区分为s1和s2区 每当进行垃圾回收时 s1的对象拷贝到s2 然后清空s1区的所有对象
为什么Eden:s0:s1 是8:1:1?
如果Eden区不够大 那么无法存储太多对象 那么会提前触发youngGC 频繁占用业务系统资源与时间
分配担保 :若进入堆中的对象大于Eden区所能容纳的最大 那么该对象会直接进入Old区
老年代GC OldGC
新对象申请内存空间
首先判断Eden区是否空间足够 够--》直接分配对象到Eden区
不够--》进行youngGC--》是否有足够空间--》够 分配到Eden区
--》不够 Eden区----不够---》old区
FullGC=yongGC+oldGC+MetaSpeace(系统资源)1.8后
permSpace(1.8之前) 持久代 jvm资源
1.8之后为什么要有MataSpace?----》避免占JVM资源(如果 类信息 常量池太大 占项目大多数资源) 项目启动 就进行FullGC

垃圾回收算法
引用计数法 --》判断一个对象是否存在引用 但是会造成循环引用 导致内存泄露 内存泄露的堆积会导致内存溢出----》但是引用计数法效率快
可达性分析算法---》我们会从一个根的位置触发 衍生出一条引用链 进行搜索 搜索当前引用链上所有对象
什么是GCRoot(一组活跃的指针 指向堆内存的地址) :静态变量 常量 栈帧中局部变量表中的元素 JNI(java native interface) 也可以作为GCroot
判定为GC不可达对象 那么 这个对象就真的背叛死刑了吗? 不是
如果该对象重写的finalize方法 并且在finalize()方法中建立我们与跟的联系 那么就会重新进入应用阶段
图片服务器一般会重写finalize方法
如何判断对象已死?---》GC不可达且没有重写finalize()方法
标记清除算法: 缺点很慢 会产生内存碎片
而且在标记清除之间会出现 对象与引用的重新建立或者删除而导致不该回收或者该回收的没有回收
造成这种问题的原因是因为 业务线程和GC线程来回切换 并行抢cpu资源
1 标记: 找到你所有的GCroot 找到所有GC可达对象并标记
2 清除 递归遍历全堆 把所有对象中没有被标记的对象全部清除
为了解决以上问题
可以将它做成串行的GC (Stop the world) 判断一个垃圾收集器的好坏 根据stop the world 时间长短

复制算法: 效率快 但代价太大 每次会浪费二分之一内存

标记清除整理算法 先标记 再清除 后整理



滑动整理算法---
分代收集算法 我们将对象的存活周期分为新生代和老年代
新生代比例8:1:1--》复制算法
老年代 标记清(CMS)除或者标记整理
4核8G以下 G1跑不了
8核16G以下 ZGC 性能达不到最高
JVM 预调优
1 上线前内存配置是否符合并发调优 调整 在并发期间不产生GC
2 压测的吞吐 如果是大项目 95%以上 一般项目 98%以上 GCview
3 FullGC 频率以及 yongGC频率
4 观测 可靠性观测 MAT分析 压测前和压测后 形成对比图 对比内存变动情况 是否上线后发生内存泄露
5 垃圾收集器参数 1 标记线程数量 2什么模式 mixed mode G1停顿时间一般是100MS-200MS之间
6 CPU的使用率增加 死锁 卡顿
出现fullGC 不正常(最好一次都不出现)
yongGC(mixed mode) 频率的稳定值 如果日志中yongGC的值超过稳定值的30% 那yongGC肯定有问题

浙公网安备 33010602011771号