JVM-对象创建出来的过程

224 阅读5分钟

(个人理解有不同看法可以讨论下哦)

 对象创建出来可以分为两步
 1、类加载
 2、对象创建

1、类加载

类加载的时候 分五步 1、加载 -> 2、验证 -> 3、准备 -> 4、解析 -> 5、初始化

1、加载

  通过io从磁盘上找到字节码文件,类使用的时候才会加载,比如调用类main方法时,
  在加载阶段会在内存中生成一个代表这个类的class对象,作为方法区这类的各种数据的入口
  
  其中类加载的时候是jvm虚拟机加载launcher,然后launcher再加载extclassload和appclassload加载器
  

类加载器有四种

  1. 引导类加载器(负责加载支撑jvm运行的位于jre的lib目录下的核心类库,比如rt.jar,charsets.jar)
  2. 扩展类加载器:加载jre的lib目录下的ext扩展目录中
  3. 应用程序类加载器:加载classpath路径下的包,加载开发者写的类
  4. 自定义加载器:加载自己指定位置的类

其中类加载的时候有一个概念双亲委派机制,就是比如应用程序类加载器加载一个类要先委托给父类加载,父类加载不到再让子类加载

用双亲委派机制有两个好处:

1、沙箱安全机制,防止核心类库被修改

2、避免类重复加载,如果父类加载了子类就不用加载了

tomcat为什么打破双亲委派机制呢

  1. 一个web容器可能需要部署两个系统,但是两个系统依赖的jar包版本不同
  2. web容器支持jsp文件修改热部署

注意:同一个JVM内两个相同包名和类名对象可以共存,因为类加载器可以不一样,只有类加载也一样的时候才能认为是同一个类

2、验证

   验证字节码文件的正确性
   

3、准备(分配内存)

   为静态变量分配内存,并赋予默认值,到这一步类已经加载完成,下面就是给对象分配内存了
   

4、解析

   将符号引用替换为直接引用,比如将main方法替换为指向数据在内存中的指针或者句柄
   这个就是静态链接的过程(在类加载阶段完成)
   动态链接在程序运行期间将符号引用替换成直接引用
   

5、初始化

   对类的静态变量初始化,执行静态代码块
   

2、对象创建

对象创建流程呢是 1、类检查(如果类没有加载先加载类) -> 2、分配内存 -> 3、初始化 -> 4、设置对象头 -> 5、执行init方法

1、类检查

  虚拟机执行遇到new 对象 的时候先去检查这个指令的参数在常量池中有没有符号引用,并且检查这个符号引用代表的类是否已经加载,解析,和初始化过
  new指令对应到语言的层面上时,new关键字、对象克隆、对象序列化

2、分配内存

 类检查过后就要给对象分配内存了,对象需要多大空间类加载的时候就确定了,分配内存呢是从java堆中划分出来
 
 这个步骤分配内存的时候有两个问题
1. 如何分配内存
    划分内存的方法:
-    指针碰撞(bump the pointer)(默认是指针碰撞):如果java堆中内存是绝对的规整,用过的和没用过的有一个指针分界线
-    空闲列表(free list):如果java堆中的内存不是规整的,已使用和未使用的交错在一起,虚拟机就得维护一个列表来记录那些内存是可用的

2. 并发情况下,内存怎么分配
-   cas(compare and swap):循环抢内存
-   本地线程分配缓冲(Thread Local Allocation Buffer,tlab):给每个线程预先分配一块内存做缓冲
    通过­XX:+/­
    UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启­XX:+UseTLAB),­XX:TLABSize 指定TLAB大小。
    

3、初始化

  内存分配完成后,虚拟机需要将除了对象头空间内存空间都初始化为0,如果是用tlab这个内存空间初始化为0可以提前到分配tlab的时候进行。所以java对象的实例字段 不用初始化就可以使用,c++中实例要先给初始值
  

4、设置对象头

  初始化之后,就要设置对象头的必要设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄
    在HotSpot虚拟机中,对象存储分三块
    1、对象头(Header)
        对象头有两部分
          1、存储对象自身的运行时数据:哈希吗、GC分代年龄、锁状态的标志、线程持有的锁、偏向线程id、偏向时间戳
          2、类型指针:对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个实例
          
          

图片.png 这个部分有一个点GC的分代年龄最大是15 因为分代年龄的空间是4bit 2、实例数据(Instance Data) 3、对齐填充(Padding)

5、执行init方法

从语言层面上来说就是为属性赋值,这个赋值是程序员给的值,和构造方法