?对象在JVM中是如何存储的对象头中有什么??作为Java开发者,我们的日常生活中可能没有对象,但是我们每天在工作中都会创建大量的Java对象。你有没有试过,你想了解你创造的这些“对象”吗?让我们从四个方面重新认识自己的“客体”。创建对象的6种方法。JVM中创建对象时发生了什么。JVM中对象的内存布局。对象的访问和定位。创建对象的最常见和最常规的方法也是最简单的方法。通过使用这个方法,我们可以调用任何我们想调用的构造函数(默认是使用无参构造函数)Personp=newPerson();使用Class类的newInstance(),只能调用空参数的构造函数,权限必须是public//获取类对象ClassaClass=Class.forName("priv.starfish.Person");Personp1=(人)aClass.newInstance();Constructor的newInstance(xxx),对构造函数没有要求.newInstance();clone()深拷贝,需要实现Cloneable接口并实现clone(),不调用任何构造函数Personp3=(Person)p.clone();反序列化通过序列化和反序列化技术从文件或网络中获取对象的二进制流。每当我们序列化和反序列化一个对象时,JVM都会为我们创建一个单独的对象。在反序列化中,JVM不使用任何构造函数来创建对象。(序列化的对象需要实现Serializable)//准备一个文件,用于存储对象的信息/序列化对象并写入磁盘oos.writeObject(p);//反序列化FileInputStreamfis=newFileInputStream(f);ObjectInputStreamois=newObjectInputStream(fis);//反序列化对象Personp4=(Person)ois.readObject();第三方库ObjeneslsJava已经支持通过Class.newInstance()动态实例化Java类,但这需要Java类有合适的构造函数。很多时候Java类不能这样创建,例如:构造函数需要参数,构造函数有副作用,构造函数抛出异常。Objenesis可以绕过上述限制。这里只针对普通Java对象讨论创建对象的步骤,不包括数组和Class对象(普通对象和数组对象的创建指令不同。创建类实例的指令:new,创建数组的指令:newarray,anewarray,multiawarray).newinstruction当虚拟机遇到一条新指令时,首先检查这条指令的参数是否可以在Metaspace的常量池中定位到某个类的符号引用,并检查该符号引用所代表的类是否已经被加载、解析并初始化(即判断分类器信息是否存在)。如果不是,则必须先以双亲委派方式执行相应的类加载过程。分配内存接下来,虚拟机将为新一代对象分配内存。一个对象需要的内存大小是在类加载完成后才完全确定的。如果实例成员变量是引用变量,则只分配引用变量空间,大小为4字节。有两种分配方式:“BumpthePointer”和“FreeList”,取决于使用的垃圾收集器是否具有compaction功能。如果内存是规则的,使用“指针碰撞”为对象分配内存。意思是所有已用内存在一侧,空闲内存在另一侧,中间放一个指针作为分界点的指示。分配内存只是将指针移动到空闲的一侧,距离等于对象的大小。如果垃圾收集器使用基于Serial或ParNew的压缩算法,则使用此方法。(一般使用带排序功能的垃圾收集器,使用指针碰撞。)Java指针碰撞。如果内存不规律,虚拟机需要维护一个列表。该列表将记录可用的内存。为对象分配内存时从链表中找到足够大的空间分配给对象实例,并更新链表的内容。这种分配方式就是“空闲列表”。在使用CMS等基于Mark-Sweep算法的收集器时,通常使用空闲链表。Javafreelist分配?我们都知道堆内存是线程共享的,所以在分配内存的时候会存在并发安全问题。JVM是怎么解决的呢??一般有两种解决方案:将分配内存空间的动作做同步处理,采用CAS机制,配合失败重试的方法保证更新操作的原子性每个线程在Java堆,然后直接在自己的“私有”内存分配中给对象分配内存,当这部分区域用完了,再分配新的“私有”内存。这种方案称为“TLAB”(线程本地分配缓冲区)。这部分Buffer是从堆中分出来的,但是是本地线程独享的。这里值得注意的是,我们说TLAB是线程独享的,但是在“分配”这个动作中是线程独享的,在读、垃圾回收等动作中是线程共享的。而且使用上没有区别。另外,TLAB只作用于新一代的EdenSpace。对象在创建的时候首先放在这个区域,但是在新生代无法分配内存的大对象会直接进入老年代。因此,在编写Java程序时,分配多个小对象通常比大对象更有效率。虚拟机是否使用TLAB是可选的,可以通过设置-XX:+/-UseTLAB参数来指定,在JDK8中默认开启。初始内存分配完成后,虚拟机需要将所有分配的内存空间初始化为零值(不包括对象头)。这一步确保对象的实例字段可以直接在Java代码中使用,而无需分配初始值。程序可以访问这些字段的数据类型对应的零值。比如byte、short、long转换为对象后的初始值为0,Boolean的初始值为false。对象的初始设置(设置对象的对象头)接下来,虚拟机需要对对象进行必要的设置,比如对象是哪个类实例,如何找到类的元数据信息,对象的hashcode,以及对象的GCGenerationalage等信息。此信息存储在对象的对象头中。根据虚拟机当前的运行状态,比如是否开启偏向锁等,对象头会有不同的设置方式。
