JVM面试题

92 阅读3分钟

1、为何Hotspot用解释器和编译器并存的结构?

  • 当程序启动和执行的时候,解释器快,省去了编译的时间,立即执行。当程序运行后,随着时间的推移,编译器启动作用,虽然编译慢,但是编译成机器码后,执行更快
  • 编译器编译成机器码后,存到方法区需要占内存,但执行效率高。解释器省内存,总体说解释器和编译器互补

2、JVM为什么要实现两个不同的JIT(即时编译器)?

因为JVM运行有两种模式,client和server两种模式。client的JIT优化力度小、编译快

3、程序何时使用解释器?何时用编译器?

当代码成为热点代码时会用编译器,成为热点代码条件如下

  • 方法被频繁调用N次

  • 循环体被循环N次

  • N 是 1500(client) / 10000(server)

  • 方法调用必须是频繁的,如果调用时间间隔过长,调用次数将缩小一半

4、JIT都优化了哪些?

  • 公共子表达式消除
  • 方法内联:正常方法执行,没调用一个方法都会被压到栈里,方法内联就是将调用的方法合到上一个方法 里,减少了方法调用过程中压栈和入栈的开销
  • 逃逸分析:只在方法内部用。以下都是没有逃逸的优化
  • 对象在栈上分配内存(正常是在堆上的)
  • 变量替换:用方法的局部变量替换对象的成员变量
  • 同步锁消除:经典例子SringBuffer

5、是不是所有的对象和数组都会在堆内存分配空间?

不一定,在JIT编译期间,如果经过逃逸分析,发现对象没有逃逸出方法,对象可能会被分配到栈内存上

6、在操作Long 和Double类型数据的时候,存不存在数据安全问题

Long 和Double类型占8个字节,被拆分为两个独立的4个字节,高四位低四位

为什么分开,因为操作系统有32位和64位(cpu操作指令数据的大小),字节码指令变成两个指令,非原子操作

7、哪些字⾯量会进⼊常量池中

  • 【final修饰】的8种基本类型成员变量
  • 【非final修饰】的包括static 8种基本类型,只有 Long、double、float进入常量池
  • 用双引号引起来的字符串值

8、类加载阶段为什么没有将全部的符号引用都替换为直接引用

因为在类加载时候解析不出,具体调用的方法,比如用多态,不知道具体是哪个类的方法

9、创建对象的过程

.class文件 load到内存中 linking 初始化 完成Class对象的创建

开辟内存空间 成员变量赋默认值 调用构造方法(成员变量顺序赋值、执行构造方法语句)

10、对象在内存中的布局

普通对象:对象头、class指针、实例数据、填充对齐

数组:对象头、class指针、数组长度、实例数据、填充对齐

class指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节,默认开启

实例数据中的引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节,默认开启

内存超过32G,开启无效

11、对象头具体包括什么

锁定的信息:synchronize的信息

GC标记:被回收多少次,分代年龄。GC标记在头上占4位,4位最大的数是15,所以GC默认年龄是15

12、对象怎么定位

句柄池

直接指针(hotspot)

13、对象怎么分配

14、Object o = new Object在内存中占用多少字节

  • Object 16个字节
  • 头部 8字节
  • Class 指针 8字节 默认打开压缩 4字节
  • 填充 4字节(8的倍数)