likes
comments
collection
share

你可能不太知道的JVM中Java 对象的生命周期

作者站长头像
站长
· 阅读数 14

本文源自Recently祝祝,创自Recently祝祝。转载请标注出处。

此解决方式在企业中有所应用,适合Java初级开发学习,参考。

本文字数与行数,耐心阅读必有收获

你可能不太知道的JVM中Java 对象的生命周期

1)对象的创建过程

对象的创建过程--》首先执行new指令创建一个对象--》对象创建好之后---》首先进行常量池检查的部分,检查一些固定不变的部分时候能在内存中被找到,检查类符号的应用时候存在---》检查类是否被加载,类的父类,方法没有被加载,则先进行类加载---》方法进栈之后以栈帧的形式存在,一行一行的执行编译后的字节码代码---》进行分配内存空间,分配内存空间主要指的是分配堆内存空间--》为刚刚分配的内存空间进行初始化,初始化设置的值为0---》设置一些必要信息,对象头数据的设置,例如对象的GC年龄、对象锁状态,对象的哈希码值、对象的类中元数据指针等信息--》这些对象的构造函数,构造对象,初始化对象

2)对象的内存分配

对象的内存分配存在两种方式:之间碰撞(Bump the Pointer),空闲列表(Free List)

第一种指尖碰撞:

  • 指尖碰撞分为已分配内存跟未分配内存,并且内存是连续的。
  • 存在一个指针指向已分配内存的最后一个位置,使用的时候,需要将指针与内存进行碰撞到未被分配内存的位置,作为新对象的起始地址。
  • 比较适合固定大小的对象内存分配。

第二种空闲列表:

  • 空闲列表分为已使用的内存跟未使用的内存,内存空间不连续,使用的时候需要遍历列表找到空闲的区域分配内存。若一块内存不够存放该对象,就会使用多块未被使用的内存存放。
  • 空闲列表的分配方式每次都需要遍历列表,存在对性能的影响。

对象的内存分配

  • 新生代对象:新建对象大多数默认进入新生代的Eden区域,不是全部,部分大对象会直接进入老年代
  • 老年代对象:
    • 根据新生代存活年龄到达15次,(会由对象头的一个计数器维护)的会进入老年代
    • 根据内存判断:Minor GC之后,Survivor1区域的对象总大小超过survivor1区种大小的百分之五十,就将年龄最大的对象放入老年代中
    • Serial和Parnew收集器的情况下,大对象直接会进入老年代
    • 年轻代垃圾回收之后,对想太多无法进入Survivor区域会直接进入老年代

3)对象的销毁过程

  1. 一个对象的销毁过程,一般说的是这个对象没有被引用后,进行垃圾回收的过程,回收这个对象所占用的内存。并且因为内存不是不断增大的,但是数据是会不断增多的,所以再一定时间内需要对没有被使用的垃圾对象进行垃圾回收,提高内存的可用空间。
  2. 在JVM中会通过引用计数法和根里达算法的方式进行垃圾对象定位,根可达算法定位之后将经过两次标记,第一次是根可达分析之后,第二次没有与finalize()重新建立连接的进行第二次标记,两次标记后,将被回收。
  3. 回收的方式有三种:标记清理算法、拷贝算法、标记整理算法。老年代使用标记清理或者标记整理。年轻代使用拷贝算法。

4)对象的访问方式

对象的两种访问方式:句柄、直接指针

句柄:

  • 通过句柄访问对象,返回对象实例指针以及对象类型数据指针
  • 通过指针定位成员变量和方法
  • 对象被移动只需要修改句柄中的地址
  • 不需要自己关系内存的分配和释放,灵活性得到提高

直接指针:

  • 直接返回对象类型数据,不通过指针的方式进行,直接指针以及指向对象数据类型指针,被访问后直接返回数据
  • 节省了指针带来的开销、访问速度快
  • 需要自己管理内存的分配和释放
  • 同时直接访问可能出现安全问题,因为可以访问对象在内存中的位置和大小等底层的细节

5)内存担保

  • 当新生代有对象要进入老年代,老年代对大对象需要进行一次内存担保,判断一下老年代时候又足够的空间可以存放下这个对象。
  • 年轻代的数据满了之后,不会直接进行内存溢出的报错,这样的报错也是错误的,所以年轻代对象满了之后,就会进行根据规则判断进入老年代,如果这个数据在老年代里边也不能存放,就会进行Full GC操作。那么要是在年轻代大对象进入到老年代之前,虚拟机先进行内存担保,先判断这个对象可以存放,保证有足够的空间可以存放这个对象,那么就可以避免频繁的进行Full GC 导致的系统性能降低。这就是内存担保的作用

文章全为个人理解,如果发现部分跟你所知道的有出入,欢迎在评论区指出,欢迎探讨。

转载自:https://juejin.cn/post/7226579885003620410
评论
请登录