问题1:解释一下对象的创建过程;

先看以下代码:


public static void main(String[] args){
Object o = new Object();
}

此方法产生以下字节码文件:


0 new #5
3 dup
4 invokespecial #1 >
7 astore_1
8 return

上述字节码中最重要的是1,3,4条,顺序为:

1、在内存中分配一部分空间存放对象

2、进行数值的初始化,通常为0或者null

3、进行构造方法的赋值

4、将o与new出来的Object进行关联

问题2:DCL(double check lock)写法中需不需要添加volatile;

volatile关键字使用主要有两点:

1、保持线程可见性


private static volatile boolean flag = true;
public static void main(String[] args){
new Thread(()->{
while(flag){
}
System.out.println("end");
},"server").start();
Thread.sleep(1000);
flag = false;
}
在执行时flag在内存空间中,如果不添加volatile关键字,即使内存中的数据改变为false,并不会影响当前线程的值,依旧为true
于是此线程绝对不会打印end
如果添加volatile以后,内存中flag改变为false,此线程会读取内存中的数据,才会打印出end

2、拒绝指令重排序

在Java代码中的执行顺序默认是顺序结构,但是有一定的几率会变成乱序结构,这种一般由系统控制,原因是内存的效率是十分低的,一般为cpu的1/100,所以系统会将完全没有联系的两个代码,将效率高的放在前面执行,内存中效率低的放在后面执行,由于两个代码彼此之间没有联系,一般看不出区别,但是总有可能影响结果:


public class Test1 {
private static volatile int x = 0,y = 0;
private static volatile int a = 0,b = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for (;;){
i++;
x = 0;y = 0;
a = 0;b = 0;
Thread one = new Thread(new Runnable() {
public void run() {
a = 1;
x = b;
}
});
Thread two = new Thread(new Runnable() {
public void run() {
b = 1;
y = a;
}
});
one.start();two.start();
one.join();two.join();
String result = "第"+i+"次("+x+","+y+")";
if (x==0 && y==0){
System.err.println(result);
break;
}else {
}
}
}
}
如果不加volatile,代码可能会出现x,y都等于0的情况,但是按照正常执行顺序,x,y是不可能都等于0的,
所以volatile关键字可以实现拒绝指针重排序的功能,一定按照代码顺序执行

DCL写法是单例模式的一种写法

普通的单例模式写法是先创建好一个对象,然后有其他方法需要时避免创建新的对象,但是在多线程高并发涉及以后,可能两个方法同时访问,识别为空,然后创建出两个对象,正确写法(spring源码也使用此写法)为:


public class Test2{
private static volatile Test2 test;//此时Test2 test必须要添加volatile关键字
private Test2{}
public static Test2 getInstance(){
if(test==null){
synchronized(Test2.class){
if(test==null){
try{
Thread.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
test = new Test2();
}
}
}
}
}

问题3:对象在内存中的存储布局;

对象的储存布局分为两个部分:

美团、饿了么面试绝命7问,你能接几招_第1张图片

 其中markword为默认的8个字节,类型指针也是4个字节,对齐就是当其余的字节数不被8整除,会自动补齐字节为8的倍数,而数组还有数组长度

问题4:对象头具体包括什么;

对象头markword主要由3点组成:

1、身份信息hashcode(identical hashcode)

2、gc垃圾回收器

3、synchronized锁的信息

问题5:对象怎么定位;

有两种定位方法:句柄方式和直接指针

美团、饿了么面试绝命7问,你能接几招_第2张图片

问题6:对象怎么分配;

优先在栈空间中分配空间,因为栈空间中不用进行垃圾回收,使用完了以后会直接回收,没有回收的话会进行FullGC回收进入Old老年区;如果对象过大,会经过本地线程缓存然后进入伊甸区,作为新生对象,进行第一轮垃圾回收,如果在第一轮YGC后没有回收,会进入存活区1,然后重复YGC,没有回收会进入存活区2,此方式使用拷贝算法;在存活区1,2中返回回收还没有回收的话,会进入Old老年区,继续进行FullGC。

问题7:Object o = new Object();在内存中占用多少字节;

不算o的情况下占用16字节:

markword有8字节,类型指针4字节,不能被8整除,最后丢失补全4字节,为8的倍数。

如果处理器在32G内存以下,会进行压缩指针算法,将o的字节压缩为4位,所以算上o以后占用20字节。

如果在32G以上,压缩指针算法失效,o的字节压不压缩都是8字节,于是在内存中占用24字节