深入理解Java虚拟机之(一):对象如何创建以及相关问题

    从语言方法面来说,创建对象的过程其实就是new对象的过程。

    (一)创建前准备:
    当Java虚拟机遇到字节码new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载解析和初始化过。如果没有则必须先执行相应的类加载过程。

    (二)创建对象:
    示意图:
在这里插入图片描述
    (三)内存分配方式:
    内存分配示意图:
        (1)指针碰撞:
在这里插入图片描述
        指的是Java堆中内存是绝对规整的,所有被使用过的内存都房子一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把指针向空闲方向挪动一段与对象大小相等的距离。

        (2)空闲列表:
        指的是Java堆中内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,这时就没办法简单的指针碰撞了,虚拟机就必须维护一个列表,记录哪些内存是可用的,哪些是不可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

    决定哪种方式的关键:
    选择哪种分配方式是由Java堆是否规整决定的,而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理的能力决定。因此当使用Serial,ParNew等压缩整理过程的收集器时,系统采用的分配算法是指针碰撞,既简单又高效。当使用CMS这种基于清除算法的收集器,理论上就只能采用较为复杂的空闲列表来分配内存。

理论上何解?
强调理论上是因为在CMS实现里面,为了能够在多数情况下分配的更块,设计了叫做Linear Allocation Buffer的分配缓存区,通过空闲列表拿到一大块分配缓冲区后,在它里面任然可以使用碰撞方式分配。

    (四)并发线程安全问题:
    对象创建在虚拟机中是非常频繁的行为,即使仅仅修改一个指针所指向的位置,在并发情况下也不是线程安全的,可能出现正在给给对象A分配内存,指针还没来得及 修改,对象B又使用原来的指针修改分配内存情况。
    两种解决方式
(1):对分配内存空间的动作进行同步处理,实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性。
(2):把内存分配的动作按照线程划分不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓存(TLAB)。哪个线程要分配内存,就在哪个线程的本地缓存中分配,只要本地缓存区用完了,分配新的缓存区时就需要同步锁定。虚拟机是否使用TLAB,可以通过参数 -XX:+/-UserTLAB参数来设定。

发布了163 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_41987908/article/details/103921263