关于对象

六、一个对象创建的过程

一个对象的创建大概分为以下几步

image-20200801171257750.png

当我们程序在碰到new这个关键字的时候,就会创建一个对象,那么大概是一下几步:

  • 检查加载

    检查我们这类是否被加载(app加载器),当然了还有很多的检查以后细说

  • 分配内存

    分配内存大概有一下两种方式

    • 执行碰撞

      我们都知道堆空间是化分的一块区域,那么如果我们之前维护的所有的对象(绝大多数的对象都是在堆空间上的,除非进过逃逸分析后,得出该对象无法逃出当前线程,那么就是栈内对象)都是正气的摆放在堆内存中,那么下次分配对象的时候,就可以很轻松的知道这个对象应该放在哪里,就是我们的指针向后移动来分配内存。

    • 空闲列表

      我们知道,垃圾回收有标记清楚算法和标记整理算法,清楚算法的话我们知道会想某一块的内存回收,但是不进行整理,那么我们就没有办法知道哪些内存是可以用的,所以我们需要维护一张空闲列表,连标记哪些内存是可用的

    • 内存分配的问题以及解决方案

      在之前的介绍中,我们知道jvm天生就是一个多线程的环境,所有我们分配内存的时候天然的就会存在线程安全问题。什么问题呢,线程a和b,a线程读取内存地址0001,可用,b线程同时也读取0001可用,然后a线程开始初始化自己对象,并且把引用返回,但是b线程在a线程稍后的时间中,同时初始化自己的对象,那么就会造成混乱。所以提出了解决方案

      • CAS加失败重试

        Compare and Swap

        [图片上传失败...(image-847ac6-1597672641022)]

        我每次需要分配的时候都需要和上传读取的内存信息做比较,这样就能保证线程的安全,就像是数据库的乐观锁思想:

        update table1 set a = 'new' where a = 'old'

        虽然这个中锁比起悲观锁效率要搞上不少,但是还是耗费了性能和时间,所有才会有下面的另一种分配方式

      • 本地线程分配缓冲(TLAB)

        cas.png

当一个分配对象的线程来划分空间的时候,jvm会主动来分配一个缓存区域来tlab给当先线程单独划分区域使用,所有这个对于线程来说就是安全的,而且不会使用任何的失败重试的操作,分配对象就比价块。但是这个区域一般是有大小限制的(一般是Eden的1%),但对象超出这个大小的时候,就会采用上面的CAS机制来分配对象。

image-20200802172524972.png

从官网中,我们也了解到这种机制是默认开启的


image-20200802173019662.png

也可以自动设置TLAB的大小

  • 内存空间初始化

    我们都知道,对象的初始化时早于构造方法的,因为构造方法中有些需要用到对象的数据,那么这个成员变量就会显初始化,所以这一步就是初始化话这些属性变量,int的变量为0.....

  • 设置

    一个对象分为好几个部分

    • 对象头

      image-20200802175233481.png

      对象头保存了很多的数据,包括我们知道的synchronized也是通过对象头上的锁标识来完成的。

    • 实例数据

      就是我们平时的数据

    • 对齐填充

      因为64为的比32的块,所有我们分配对象的时候是会以8字节的整数分配对象的,不够的部分就会进行填充

  • 对象初始化

    最后一步就是我们构造方法,我们就可以使用一个对象了。

七、对象的引用

我们的代码中,不可能直接去获取一个对象的内存地址来使用,都是拿到一个引用地址,然后jvm会帮助我们去调用方法等等。。。,那么我们如何获取对象的引用呢,现在主流的有两种方式

  • 使用句柄

    句柄拥有句柄池,句柄池中的数据执行会和jvm对象进行一一对应,然后我们就不需要获取对象的指针,我们只需要拿到一个永远不变的句柄引用就好,这种思想我认为是很有必要的

    image-20200802191556996.png

    原因:

    • 我们知道大部分的gc都会使用标记整理算法,这就意味着改变了对象的直接地址,那么我们在最gc的时候,就必须stop the work,来确保正在执行的程序对象引用的正常。

    • 那么使用一个不会变动的句柄池,这样就保证了我们对象地址变动时候的安全性

    缺点:

    • 所谓的缺点也很明显,我们都知道内存是稀有且珍贵的,那么专门划分一个区域的句柄池,这样确实会造成不少的空间的浪费

    • 同事,效率也缺乏,这个就像是我们本来只需要一个单表查询,然后现在我非得left join一下我才能找到对象

  • 直接指针

    基于句柄有不可逾越的性能问题,所有我们的主流的虚拟机都是去使用直接指针。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。