Java中对象是怎么回事?

Java中对象是怎么回事?

1.对象在内存中的存储?

​ 当我们new一个Java对象时,new出来的Java对象都包含了什么东西,每部分都占多少字节,作为Java工程师还是有必要了解这个东西的。

​ 一个对象包含以下四部分:

  • markword(8字节):用来存放对象hashcode、锁状态、锁标志位等信息
  • 类型指针(4字节):对象是什么类型的,指向T.class
  • 实例数据:类的成员变量所占的内存
  • 对齐:一个对象所占字节数要被8整除,否则需要对齐
    在这里插入图片描述

​ 以下是使用JOL(Java Object Layout)分别将一个空对象和有成员变量的对象的内存结构:

空对象

​ 可以在图片中看到,markword8个字节,类型指针4个字节,一共12个字节,不能被8整除,所以有对齐4个字节,即图中的“loss due to next object aligment”,当加上一个Integer类型的成员变量之后,刚好一共16个字节,就不需要对齐了,大家有兴趣可以试试。

public class test {
//    Integer i;
//    String s;
     public static void main(String[] args) {
        test t=new test();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
    }
}

在这里插入图片描述

有成员变量的对象

public class test {
    Integer i;
    String s;
     public static void main(String[] args) {
        test t=new test();
        System.out.println(ClassLayout.parseInstance(t).toPrintable());
    }
}

在这里插入图片描述

1.1 面试题

​ 以下对象在内存中占用多少个字节?

object o=new Object();

​ 上面讲过,对象中一共有四部分,分别是markword(8字节)、类型指针(4字节)、实例数据和对齐,Object中是没有属性的,所以没有实例数据,但由于对象头+类型指针一共只有12个字节,不能被8整除,所以需要对齐4个字节,一共占用16个字节,以下是代码和结果:

public class test {
     public static void main(String[] args) {
         Object o=new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

在这里插入图片描述

2.对象头是什么?

​ 在上面截图当中有一个object header,翻译过来就是对象头,其实就是markword+类型指针,一共12个字节。

​ 对象头中最重要的就是markword,其中包含了hashcode、锁状态、锁标识位等信息,主要分为三大块:

  • 锁信息
  • hashcode
  • GC信息

​ 下面以加synchronized锁为例,看看markword是怎么变化的,代码和结果如下:

public static void main(String[] args) {
       //加锁之前
       Object o=new Object();
       System.out.println(ClassLayout.parseInstance(o).toPrintable());
      //加锁之后
       synchronized (o){}
       System.out.println(ClassLayout.parseInstance(0).toPrintable());
       //解锁之后
       System.out.println(ClassLayout.parseInstance(0).toPrintable());
  }

在这里插入图片描述

​ 可以明显地看到,加锁前和解锁后的markword是相同的,加锁之后markword不一样。

​ 除了加锁之后markword会变化,在调用对象的hashcode()方法之后,markword也会发生变化,因为markword中会记录对象的hashcode值,大家也可以试一试。

3.对象如何定位
  • 句柄方式(间接方式)

    ​ 对象引用指向堆中的句柄池,然后在句柄池中分别获取对象地址类型地址,以此来找到对应的对象数据和类型数据。

    ​ 这样的缺点就是多浪费了一次寻址开销,但当对象发生改变时,o的地址不需要改变,只需要改变句柄中的地址信息,会使GC更高效。
    在这里插入图片描述

  • 直接指针(直接方式)

    ​ 对象引用指向堆中的对象,对象中存储着指向方法区的类型数据,可以通过找到对象获取类型指针,然后再获取类型数据。

    ​ 使用直接方式的优点就是速度快,相比于句柄方式节省了一次寻址的开销,这种方式也是hotspot中所用的,但是当对象发生改变时,o的地址也需要改变。
    在这里插入图片描述

4.对象如何分配?
目前使用的GC算法都是分代收集算法,那么也就可以知道整个堆是被分为了两部分,分别是新生代和老年代,不同类型的对象放在不同的地方。

​ 一般来说,新创建的对象都会放在新生代中,有以下几种情况会直接放到老年代中或移动到老年代中:

  • 当创建的对象所需连续内存太大时,则会放到老年代中
  • 当新生代中的对象年龄超过15(默认)时,就会将对象移动到老年代中
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值