Java虚拟机 之 对象

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yichen97/article/details/90730866

引言

本篇主要围绕对象的创建、内存分配、结构、访问定位来说的,对有些规则不理解的可以先查看上篇JVM内存区域

对象的创建过程

通过new创建的对象,这个对象就存储在堆中。具体步骤如下:

如何给对象分配内存?

分配内存有两种方式,分别是指针碰撞和空闲列表。

1. 指针碰撞

前提是假设内存是规整的,用过的内存放在一边,空闲的内存放在一边,而中间拿一个指针作为分界点指示器。那么,分配内存就是指针移动的过程。

2. 空闲列表

但Java堆并不规整的,使用的内存和为使用的内存可能是相互交错的,所以有的时候无法使用指针碰撞。

所谓空闲列表就是,Java虚拟机必须维护一个列表,记录哪些内存哪些内存块是可用的,在分配的时候就可以从这张表中找出可用的一块区域给这个对象的实例,并更新在这一个表中。

那么,应该如何选择这两种内存分配方式?

选择内存的分配方式是通过Java堆是否规整来决定的,而Java堆的是否规整是通过垃圾回收策略来决定的。

垃圾回收器如果带有压缩、整理的功能,它就会在垃圾回收过程中,自动进行压缩整理,把内存区域划分成非常有规则的空间,那么,就可以使用指针碰撞;如果垃圾回收器没有这个功能,那就不能使用指针碰撞,必须使用空闲列表。

对象创建时的线程安全性问题

不论是使用指针碰撞,还是空闲列表的方式,都会存在线程安全性的问题。

指针碰撞:如果在同一时刻,多个对象在同时创建,那么,在指针移动的过程中,可能就会出现线程安全性问题;

空闲列表:第一个线程请求分配内存,发现有一块内存区域是空闲的,就把这块内存分配了,但还没来的及更新这个表。这时候,第二个请求过来,以为这块内存还是空闲空间,所以就把原来分配的那块区域给占用了。

如何解决线程安全性问题?

1. 加锁,实现线程同步。

当一个线程进来,加锁,当这个线程执行完毕后,下一个线程才让进来。

这种方式虽然安全,但执行效率太低。

2. 本地线程分配缓冲。

针对每一个线程,在堆内存中单独给它分配一块内存区域,容量可以通过虚拟机参数进行指定,成为本地线程分配缓冲(Thread Local Allocation Buffer)。

当A线程来进行内存分配的时候,就在A线程自己的区域来进行分配;另一个线程也在另外的线程区域进行分配。每一个线程操作不同的区域,就不会导致线程安全性问题。

如果A线程的内存区域满了,可以再给他分配一块内存,在这块内存分配的时候,需要对他进行同步策略(加锁)。

对象的结构

对象的访问定位

对象的访问定位有两种方式,分别是使用句柄和直接指针。

1. 使用句柄

使用句柄的方式并不是直接指向了内存区域,而是指向堆中的一块区域(句柄池)。在句柄池中,保存了实例对象的地址。

好处就是栈内存中所存储的地址永远不会改变,变的只是句柄池。

2. 直接指针

引用类型直接指向真正的内存区域。

好处是速度快,因为减少了一次寻址过程。

猜你喜欢

转载自blog.csdn.net/yichen97/article/details/90730866