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的倍数)