Skip to content

Latest commit

 

History

History
34 lines (34 loc) · 3.97 KB

对象.md

File metadata and controls

34 lines (34 loc) · 3.97 KB

1. 对象的创建

  • java虚拟机遇到一个new指令
  • 检查new引用代表的类是否被加载,解析和初始化
    • 加载过
    • 没有加载过,先执行相应类的加载过程
  • 虚拟机为对象分配内存
    • 对象所需要的内存大小在类加载过后便可以确定
    • 为对象分配空间的过程等同于把一块确定大小的内存从java堆中划分出来。
      • java堆绝对规整
        • 绝对规整解释:所有用过的内存放在一边,空闲的内存放在另一边中间放着一个指针作为分界点的指示器。
        • 分配过程:指针向空闲空间那边挪动一段与对象大小相等的距离。这种分配方式称为“指针碰撞”。
      • java堆不规整
        • 不规整解释:已使用的内存和未使用的内存相互交错,虚拟机维护一个列表,记录那些内存是可用的。
        • 分配过程:分配时从列表中找一块足够大的空间划分给对象实例。并更新列表上的记录。这种分配方式称为“空闲列表”。
      • java堆是否规整是由所采用的垃圾收集器是否带有压缩整理功能决定的。
  • 对象创建时并发问题
    • 描述:对象创建在虚拟机中是非常频繁的,因此在并发情况下是线程不安全的。可能指针正在为A对象分配内存,对象B又同时使用了原来的指针来分配内存。
    • 解决方案
      • 第一种方式:
        • 分配内存的动作进行同步处理
      • 第二种方式:
        • 把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲。哪个线程要分配内存就在哪个线程的TLAB上分配。只有TLAB用完并分配新的TLAB时才需要同步锁定。
        • TLAB解释:TLAB全称ThreadLocalAllocBuffer,是线程的一块私有内存,如果设置了虚拟机参数 -XX:UseTLAB,在线程初始化时,同时也会申请一块指定大小的内存,只给当前线程使用,这样每个线程都单独拥有一个Buffer,如果需要分配内存,就在自己的Buffer上分配,这样就不存在竞争的情况,可以大大提升分配效率,当Buffer容量不够的时候,再重新从Eden区域申请一块继续使用,这个申请动作还是需要原子操作的。
  • 虚拟机将分配到的内存空间都初始化为零值(不包括对象头),这一操作保证了对象的实例字段在java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值
  • 虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。这些信息存在对象头(Object Header)之中。
  • 执行对象初始化,即把对象按照程序员的意愿进行初始化。

2.对象的访问定位

  • 前面的学习我们知道,java虚拟机栈保存的是对象的句柄或者对象地址。所以访问对象需要通过栈上的局部变量表(reference)来操作堆上的具体对象。因为reference只是一个只想对象的引用,并没有定义这个引用应该通过什么方式定位,访问堆中的对象的具体位置,所以对象访问的方法取决于虚拟机的实现方式。目前主流的访问方式有以下两种
    • 句柄式:
      • java堆中有一块内存作为句柄池,reference存储的就是对象的句柄地址。句柄包含了对象实例数据与类型数据各自具体的地址信息。
      • 优点:对象被移动时(垃圾回收时,整理内存时,很有可能被移动)只会改变句柄中的实例数据指针,不用改变reference
    • 直接指针访问:
      • reference存储的直接就是对象地址
      • 优点:快,省略了一次句柄到具体位置的访问时间。因为对象比较多,所以这个时间很可观