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

先看以下代码:

public static void main(String[] args){

Object o = new Object();

}

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

0 new #5 <java/lang/Object>

3 dup

4 invokespecial #1 <java/lang/Object.<init>>

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问,你能接几招_jvm
其中markword为默认的8个字节,类型指针也是4个字节,对齐就是当其余的字节数不被8整除,会自动补齐字节为8的倍数,而数组还有数组长度

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

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

1、身份信息hashcode(identical hashcode)

2、gc垃圾回收器

3、synchronized锁的信息

问题5:对象怎么定位;

有两种定位方法:句柄方式和直接指针
美团、饿了么面试绝命7问,你能接几招_编程语言_02

问题6:对象怎么分配;

美团、饿了么面试绝命7问,你能接几招_jvm_03
优先在栈空间中分配空间,因为栈空间中不用进行垃圾回收,使用完了以后会直接回收,没有回收的话会进行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字节

感谢你看到这里,我是程序员麦冬,一个java开发从业者,深耕行业六年了,每天都会分享java相关技术文章或行业资讯