美团面试绝命7问答案以及解析Object 0 = new Object();

问题:

关于
Object o = new Object();
1、解释一下对象的创建过程;
2、DCL(double check lock)写法中需不需要添加volatile;
3、对象在内存中的存储布局;
4、对象头具体包括什么;
5、对象怎么定位;
6、对象怎么分配;
7、Object o = new Object();在内存中占用多少字节;

问题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问答案以及解析Object 0 = new Object();_第1张图片

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

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

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

1、身份信息hashcode(identical hashcode)

2、gc垃圾回收器

3、synchronized锁的信息

问题5:对象怎么定位;

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

美团面试绝命7问答案以及解析Object 0 = new Object();_第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字节

你可能感兴趣的:(jvm,java)