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. 坏处: 当堆中的对象实例地址发生了变化时,栈帧中局部变量表的变量引用地址也要随之变化
目录
相关文章
|
1月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
39 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
19天前
|
Java
JVM运行时数据区
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
22 2
|
25天前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
19 3
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
77 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
68 3
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
59 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
26天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
208 1
|
2月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
40 4
|
15天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
24天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80