JVM深入学习(九)-运行时数据区之对象的布局和定位

简介: JVM深入学习(九)-运行时数据区之对象的布局和定位

6.1 对象创建的方式

  1. new
  1. 单例也算new的方式
  2. 建造者模式和工厂模式产生的对象都是new
  1. StringBuilder
  2. BeanFactory
  1. Class.newInstance
  2. Constructor.newInstance
  3. clone() 需实现clonable接口
  4. 反序列化,可以从二进制流中反序列化出对象
  5. 第三方库Objenesis

测试代码:

package com.zy.study11;


import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;

import com.zy.study10.MethodAreaTest;

import org.objenesis.ObjenesisStd;


import java.io.*;

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationTargetException;


/**

* @Author: Zy

* @Date: 2021/9/1 14:55

* 测试对象创建的几种方法

* 1.new

* 1.1单例也算new的方式

* 1.2建造者模式和工厂模式产生的对象都是new

* 1.2.1StringBuilder

* 1.2.2BeanFactory

* 2.Class.newInstance

* 3.Constructor.newInstance

* 4.clone() 需实现clonable接口

* 5.反序列化,可以从二进制流中反序列化出对象

* 6.第三方库Objenesis 引包

*/

public class ObjectCreateTest {


   static class ObjectCreate implements Cloneable, Serializable {

       private int x;

       private int y;


       public ObjectCreate(int x, int y) {

           this.x = x;

           this.y = y;

       }


       public ObjectCreate() {

       }


       @Override

       protected Object clone() throws CloneNotSupportedException {

           return super.clone();

       }

   }


   public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, CloneNotSupportedException, IOException, ClassNotFoundException {

       //1. new

       ObjectCreate objectCreate = new ObjectCreate();

       System.out.println("new对象的内存地址: "+objectCreate);


       //2. Class.newInstance

       ObjectCreate objectCreate1 = ObjectCreate.class.newInstance();

       System.out.println("Class.newInstance对象的内存地址: "+objectCreate1);


       //3.Constructor.newInstance

       ObjectCreate objectCreate2 = ObjectCreate.class.getConstructor().newInstance();

       System.out.println("Constructor.newInstance对象的内存地址: "+objectCreate2);


       // 4.clone

       ObjectCreate clone = (ObjectCreate) objectCreate.clone();

       System.out.println("clone对象的内存地址: "+clone);


       // 5.反序列化

       ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

       new ObjectOutputStream(byteArrayOutputStream).writeObject(objectCreate);

       ObjectCreate unSerializable = (ObjectCreate) new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())).readObject();

       System.out.println("反序列化对象的内存地址: "+unSerializable);


       // 6.第三方库 Objenesis

       ObjenesisStd objenesisStd = new ObjenesisStd();

       ObjectCreate objectCreate3 = objenesisStd.getInstantiatorOf(ObjectCreate.class).newInstance();

       System.out.println("Objenesis生成的内存地址: "+objectCreate3);


   }

}


运行结果:


6.2 对象创建的过程

6.2.1 从字节码中看

一行简单的代码

Object obj = new Object();

查看字节码

Classfile /E:/��Ң/idea��Ŀ/jvm/target/classes/com/zy/study11/ObjectCreateProcessTest.class

 Last modified 2021-9-1; size 491 bytes

 MD5 checksum 26134ce9ee2f969bfbee2050f921671d

 Compiled from "ObjectCreateProcessTest.java"

public class com.zy.study11.ObjectCreateProcessTest

 minor version: 0

 major version: 52

 flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

  #1 = Methodref          #2.#19         // java/lang/Object."<init>":()V

  #2 = Class              #20            // java/lang/Object

  #3 = Class              #21            // com/zy/study11/ObjectCreateProcessTest

  #4 = Utf8               <init>

  #5 = Utf8               ()V

  #6 = Utf8               Code

  #7 = Utf8               LineNumberTable

  #8 = Utf8               LocalVariableTable

  #9 = Utf8               this

 #10 = Utf8               Lcom/zy/study11/ObjectCreateProcessTest;

 #11 = Utf8               main

 #12 = Utf8               ([Ljava/lang/String;)V

 #13 = Utf8               args

 #14 = Utf8               [Ljava/lang/String;

 #15 = Utf8               obj

 #16 = Utf8               Ljava/lang/Object;

 #17 = Utf8               SourceFile

 #18 = Utf8               ObjectCreateProcessTest.java

 #19 = NameAndType        #4:#5          // "<init>":()V

 #20 = Utf8               java/lang/Object

 #21 = Utf8               com/zy/study11/ObjectCreateProcessTest

{

 public com.zy.study11.ObjectCreateProcessTest();

   descriptor: ()V

   flags: ACC_PUBLIC

   Code:

     stack=1, locals=1, args_size=1

        0: aload_0

        1: invokespecial #1                  // Method java/lang/Object."<init>":()V

        4: return

     LineNumberTable:

       line 8: 0

     LocalVariableTable:

       Start  Length  Slot  Name   Signature

           0       5     0  this   Lcom/zy/study11/ObjectCreateProcessTest;


 public static void main(java.lang.String[]);

   descriptor: ([Ljava/lang/String;)V

   flags: ACC_PUBLIC, ACC_STATIC

   Code:

     stack=2, locals=2, args_size=1

     ##########重点##################

        0: new           #2                  // class java/lang/Object

        3: dup

        4: invokespecial #1                  // Method java/lang/Object."<init>":()V

     #####################################

        7: astore_1

        8: return

     LineNumberTable:

       line 11: 0

       line 12: 8

     LocalVariableTable:

       Start  Length  Slot  Name   Signature

           0       9     0  args   [Ljava/lang/String;

           8       1     1   obj   Ljava/lang/Object;

}

SourceFile: "ObjectCreateProcessTest.java"


可以看到执行的过程:

  1. new命令,用到了#2也就是object的class对象,主要作用是:
  1. 判断这个类是否已经加载到方法区了,如果没有,类加载器进行加载
  2. 在堆中为这个对象开辟内存空间
  1. dup 在栈中创建两个引用指向堆中的内存地址
  2. invokespecial 调用Object类的构造器,#1也可以从常量池中看到就是Object的构造器方法

6.2.2 对象的创建步骤

  1. 对象类型信息的加载,链接,初始化
  1. 查看元空间是否存在该类的类元信息,如果没有,使用类加载器对此类进行加载(双亲委派机制),并生成Class对象,如果加载过程中找不到对应的.class文件,就会报ClassNotFoundException.
  1. 为对象分配内存空间,根据内存空间是否规整来判断,而内存空间是否规整又跟垃圾回收器有关系,有些垃圾回收器有压缩算法,在回收后就导致内存空间不规整.
  1. 内存空间规整
  1. 指针碰撞法分配内存, 内存规整的空间,将已使用内存和未使用内存分开,用指针标记,指针碰撞法就是在分配内存空间之后将指针移动到新分配的内存空间处
  1. 内存空间不规整
  1. 虚拟机维护一个空闲列表,通过空闲列表寻找可以放下此对象的空间
  1. 处理堆内存并发问题
  1. TLAB
  2. CAS失败重试,加锁保证原子性
  1. 对象属性赋默认值
  2. 设置对象头信息
  3. 调用对象构造器,进行对象的显式初始化.

6.2.3 对象的内存布局

  1. 对象头
  1. 运行时元数据
  1. 哈希值
  2. GC分代年龄
  3. 锁状态标志
  4. 线程持有的锁
  5. 偏向线程ID
  6. 偏向时间戳
  1. 类型信息 指向该对象对应的Class对象,其实就是指向方法区中存储的类型信息
  2. 如果是数组对象,还要记录一下数组的长度
  1. 实例数据,存储对象的实际信息
  1. 存储对象的各个字段,包括父类的字段,并且父类的字段在前
  2. 如果CompareFields为true(默认true),子类的窄变量可能存储到父类变量的缝隙
  3. 相同宽度的字段总是被分配到一起
  1. 对齐填充,无实际意义

图解:


6.3 对象的定位

对象创建出来就是用来访问的

对象访问:

  1. 句柄访问
  1. 堆中维护了句柄池,句柄池中有对象实例数据地址和对象类型数据地址,栈中的引用通过句柄池中的可以访问对象实例数据和对象类型数据
  2. 好处: 当堆中的对象实例地址发生了变化时,不必修改栈中引用的地址,修改句柄池中的对象实例地址即可
  3. 坏处: 增加了访问环节
  1. 直接访问 (HotSpot使用)
  1. 如上图内存布局所示,通过栈帧中的引用直接访问对象实例地址,然后再通过对象中的类型指针访问方法区中的对象类型信息
  2. 好处: 直接访问,减少了中间环节,并且堆中不需要额外开辟句柄池
  3. 坏处: 当堆中的对象实例地址发生了变化时,栈帧中局部变量表的变量引用地址也要随之变化
目录
相关文章
|
21天前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
56 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
12天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
36 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
12天前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
18 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
12天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
28 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
14天前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
12天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
28 4
|
3月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
15天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
|
2天前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
19 10
|
1天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。