美文网首页程序员
建议看看!这几个java对象高频面试题,面试BAT肯定会碰到

建议看看!这几个java对象高频面试题,面试BAT肯定会碰到

作者: 程序员匡胤 | 来源:发表于2020-08-16 13:23 被阅读0次

    请解释一下对象创建的过程

    案例:

    public class Test {
        int m = 8;
        public static void main(String[] args) {
            Test t = new Test();
        }
    }
    
    

    使用下图插件区查看字节码:

    0 new #3 <com/java/Demo1/Test>//在内存中新开辟一块空间,并将成员变量全部设为默认值,此处将m设为0;
    3 dup//复制引用
    4 invokespecial #4 <com/java/Demo1/Test.<init>>//特殊调用Test的默认构造方法,当这条语句执行完,m的初始值就变为了8
    7 astore_1//将t的指针指向新创建的对象
    8 return
    
    

    DCL(Double Check Lock)单例到底需不需要volatile?

    单例:某个类在内存中new出来的对象只有一个
    案例1(饿汉式):

    public class Test {
        private static final Test INSTANCE = new Test();
    
        private Test(){}
    
        private static Test getInstance(){ return INSTANCE; }
    
        public static void main(String[] args) {
            Test t1 = Test.getInstance();
            Test t2 = Test.getInstance();
            System.out.println(t1 == t2);
        }
    }
    
    

    案例2(懒汉式):

    public class Test {
        private static Test INSTANCE;
    
        private Test(){}
    
        private static Test getInstance(){
            if(INSTANCE == null){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Test();
            }
            return INSTANCE;
        }
    }
    
    

    方法上上锁:

    private static synchronized Test getInstance(){
            if(INSTANCE == null){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Test();
            }
            return INSTANCE;
        }
    
    

    方法内判断后上锁:

    private static Test getInstance(){
            if(INSTANCE == null){
                sychronized(Test.class){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Test();
                }
            }
            return INSTANCE;
        }
    
    

    DCL:

    private static Test getInstance(){
            if(INSTANCE == null){
                sychronized(Test.class){
                    if(INSTANCE == null){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    INSTANCE = new Test();
                    }
                }
            }
            return INSTANCE;
        }
    
    

    volatile作用 :

    1.保持线程可见性
    2.禁止指令重排

    指令重排:
    CPU速度比内存速度要快100倍,假设有两条指令,第一条指令去内存中取一个数据,第二条指令是在CPU上做个运算,两个操作没有关联性,此时由于取数据操作相对要慢很多,CPU会先执行第二条指令,这是CPU在最底层做的优化机制。
    这就是指令重排,目的是为了提高效率。

    //int m = 8;
    0 new #3 <com/java/Demo1/Test>//int m = 0;
    4 invokespecial #4 <com/java/Demo1/Test.<init>>//int m = 8;
    7 astore_1//t = new Test();
    
    

    发生指令重排:

    //int m = 8;
    0 new #3 <com/java/Demo1/Test>//int m = 0;
    7 astore_1//t = new Test();
    4 invokespecial #4 <com/java/Demo1/Test.<init>>//int m = 8;
    
    

    对象在内存中的存储布局

    普通对象:


    数组:


    markword:8个字节,包括锁信息等
    class pointer:4个字节,指向类的class对象
    instance data:长度看具体情况,保存了成员变量
    padding:8字节对齐
    数组还多一个length长度,4个字节大小,由此也可以知道数组所能创建的最大长度。

    对象头中保存的信息

    markword:

    包含了锁信息、identity hashcode(你调用了hashcode方法就会有,不调用就没有)和GC的信息

    锁信息:

    通过JOL类库我们能够查看一个对象在内存中的布局


    没有加锁的对象内存布局:


    加锁的对象内存布局:


    class pointer:
    保存了指向类的class对象的指针,默认开启压缩是4个字节大小,不压缩是8个字节大小。

    对象怎么定位

    1.直接指针


    引用直接指向new出来的对象
    优点:直接访问
    缺点:GC需要移动对象的时候稍麻烦

    2.句柄方式

    引用会直接指向一个句柄池,句柄池包含实例数据指针和类型数据指针,实例数据指针指向new出来的对象,类型数据指针会指向类的class对象
    优点:稳定,垃圾回收时不用频繁改动引用指向
    缺点:两次访问

    对象怎么分配

    当new一个对象的时候,首先会尝试在栈中分配,当满足一些条件之后,就会将对象放入栈中(优点:方法一结束,整个栈帧往外一弹里面分配的东西就没了,不需要GC的介入效率非常高);如果没有放入栈中,它会看你的对象大小,如果足够大就会直接放入老年代,然后通过FGC回收;如果大小不够大就会放入TLAB(Thread Local Alication Buffer,线程本地分配缓冲区)中,它会往当前线程在E区内所占有的私有空间中放入对象;如果放满了,就放在E区其他内存空间中同步地放入对象,等待GC清除;如果GC清除不掉,就会进入到survival区进行循环;然后当达到一定年龄的时候就会进入到老年代,没有达到就进入一个新的survival区,继续循环直到被回收。


    Object o = new Object()在内存中占用多少字节

    8个字节markword,4个字节classpointer,4个字节用来对齐,加上默认开启压缩后引用o占4个字节,所以总共20个字节。

    最后

    感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

    相关文章

      网友评论

        本文标题:建议看看!这几个java对象高频面试题,面试BAT肯定会碰到

        本文链接:https://www.haomeiwen.com/subject/wautjktx.html