java关于对象的几个面试题

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

案例:

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

2.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;

3.对象在内存中的存储布局

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

4.对象头中保存的信息

markword:

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

锁信息:

通过JOL类库我们能够查看一个对象在内存中的布局
在这里插入图片描述

没有加锁的对象内存布局:
在这里插入图片描述

加锁的对象内存布局:
在这里插入图片描述
class pointer:
保存了指向类的class对象的指针,默认开启压缩是4个字节大小,不压缩是8个字节大小。

5.对象怎么定位

1.直接指针
在这里插入图片描述

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

2.句柄方式
在这里插入图片描述

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

6.对象怎么分配

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值