底层探究之JVM篇

2023-09-25 八股文
1.谁来负责将Class文件加载到内存?

类装载器;但类装载器只负责装在,是否可以执行由执行引擎决定。

JVM如何加载一个类

2. .class文件存在内存哪个位置

方法区;存放的类信息包括:变量名、方法名、方法代码、访问权限等等

3.Class对象存储在哪里?一个类什么时候进入JVM?

堆;

  1. 虚拟机启动时,执行main()方法的时候
  2. new对象的时候
  3. 读取静态字段或静态方法的时候
4.如下代码输出的结果是什么?
public class Main {
    static {
        System.out.println("Main static");
    }
    public static void main(String[] args) {
        System.out.println("Main main begin");
        Son a = new Son();
        System.out.println(Son.width);
        Son b = new Son();
    }
}

class Father {
    static {
        System.out.println("Father static");
    }
}

class Son extends Father{
    public static int width = 60;
    static {
        System.out.println("Son static");
        width = 30;
    }

    public Son() {
        System.out.println("Son Init");
    }
}
点击显示答案
Main static       // 执行Main的<clinit>方法
Main main begin   
Father static     // 执行Father的<clinit>方法
Son static        // 执行Son的<clinit>方法
Son Init          // 执行Son的<init>方法
30
Son Init          // 执行Son的<init>方法
5.调用一个类的final字段会触发初始化吗?调用静态方法呢?聊一下什么是类的主动引用
  1. 只调用final字段不会触发类的初始化
  2. 调用静态方法,会先触发类的初始化
  3. 反射调用也会触发
  4. 先初始化父类,再初始化子类。
  5. 详见类的初始化顺序
6.你能想到哪些引用类但没有触发类的初始化的例子(被动引用)?
  1. 通过子类引用父类static变量,例如Son.count,count是父类的变量
  2. 定义数组类,例如new Student[10]
  3. 只引用static final修饰的变量
  4. 详见类的初始化顺序
7.JVM中的程序计数器有什么用?
  • 每个线程都有一个程序计数器
  • 记录字节码指令地址
  • 当多线程切换恢复时,通过程序计数器接着执行下一条指令
8.JVM的栈与堆分别存放什么,有什么区别?
    • 存放基本数据类型、局部变量;如果是对象,则只存放地址;
    • 方法执行完毕(栈帧出栈)立即释放;
    • 由于只存在单线程,其他线程无法访问;
    • 所有线程共享
    • 用完后靠GC回收
    • 堆内对象的变量都有默认值
9.什么是短命对象?什么是长命对象?
  • 长命对象:一直被引用就是长命对象
  • 短命对象:例如方法执行完毕就没人引用,即出栈后无引用
  • 在JVM中,大部分都是短命对象;少数对象长期存活
10.什么是年轻代与老年代?

年轻代存储短命对象;老年代存储长命对象。

11.堆的内部结构是怎样的?

年轻代和老年代的内存占比是1:2,年轻代还被划分如图所示的两个区域(Eden和Surivior)

堆内存分配

12.压测理论分析:QPS=1000大概需要多少内存?
  1. 假设商品列表有10个字段,平均每个字段100字节,10*100≈1KB
  2. 访问一页是10条数据,每次访问会产生10KB
  3. 1000*10KM≈10MB,每秒大概需要10M
13.QPS=1000要保证YGC不能再一分钟内被触发,JVM参数该如何配置?
  • 年轻代内存不足的时候(Eden和From区不够)触发YoungGC,即堆内存
  • 1s=10M,那么一分钟10*60=600M
  • young:old=1:2=600:1200,堆内存需要配置为1800M
14.Object对象在堆中以什么形式存储?
  • 由4部分组成,MarkWord、class对象指针、数据内容和对其填充
  • class对象指针指向方法区,方法区记录类的信息,如字段、方法、常量等

Object在内存中如何存储

15.MarkWord是什么?记录了哪些东西
  1. MarkWord是对象头信息一部分,对象头包含MarkWord和class对象指针
  2. 记录锁状态、锁类型、锁状态、偏向锁信息

image-20230726222848669

16.你MarkWord记录了锁的信息,记录的是什么锁?什么情况会产生锁?
  • 无锁、匿名偏向锁、偏向锁、轻量级锁、重量级锁
  • MarkWord记录锁的信息,没有数据访问时是无锁001
  • 当有一个线程访问时,会产生匿名偏向锁101
17.那么匿名偏向锁和偏向锁的关系是什么?
  1. 线程第一次进入同步块,会记录线程ID,也就是使用同步代码块才会产生偏向锁
  2. 下一个线程进入代码块会判断是不是这个线程
  3. 如果不是,则尝试使用CAS替换线程ID,如果替换失败代表之前的线程还在,此时升级为轻量级锁
  4. 偏向锁的功能是在无竞争的情况下减少锁竞争的开销
18.什么时候升级成轻量级锁?
  1. 当线程访问带有偏向锁的资源,会使用CAS尝试获取
  2. 根据自适应自旋算法,自旋到一定次数还没获取到,会升级成重量级锁
19.谈谈重量级锁。
  1. 清理级锁一直自旋获取失败,会升级成重量级锁
  2. 重量级锁依赖于操作系统互斥量实现,线程进入阻塞状态
  3. 进入阻塞状态将不在销毁资源,但是唤醒需要更长的时间
上次更新: 5 个月前