java 一条数据循环两次_建议看看!这几个java对象高频面试题,面试BAT肯定会碰到...

1717e790e9510b9f4f11160d3da917e4.png

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

案例:

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

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

c2183f84664f6b1da2274ea367dff06b.png

ee5872f12b54beabb9a7f1c27593b14b.png
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;

对象在内存中的存储布局

普通对象:

67ae2ce78f623dc64828af24700327a9.png

数组:

401ae7b7c78bdf87666acf4d70c36d69.png

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

对象头中保存的信息

markword:

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

锁信息:

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

7c03612c600050199c9844dd2685b283.png

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

31558321dd4e557153b8c5f2822988cc.png

加锁的对象内存布局:

7a65a454616a8274ecd8ab2163ef0881.png

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

对象怎么定位

1.直接指针

71e35d30e39a7c88887ec601e553e78f.png

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

2.句柄方式

f704173d9f1ca29225a0058c4c8c6d52.png

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

对象怎么分配

baff5404e85c9f2104482cb2358fd124.png

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

c953d8772784df957e2e5f8c422e7bbb.png

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

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

最后

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值