欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

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

程序员文章站 2022-05-18 17:47:40
问题:关于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){ Objec...

问题:

关于
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 <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问答案以及解析Object 0 = new Object();

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

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

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

1、身份信息hashcode(identical hashcode)

2、gc垃圾回收器

3、synchronized锁的信息

问题5:对象怎么定位;

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

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

问题6:对象怎么分配;

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

优先在栈空间中分配空间,因为栈空间中不用进行垃圾回收,使用完了以后会直接回收,没有回收的话会进行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字节

本文地址:https://blog.csdn.net/PearyTeahouse/article/details/107667500

相关标签: jvm java