JVM学习(四)JVM中的对象及引用

new的过程

基本上所有的对象都是通过new关键字创建出来的,类似下面这样的

MyObject my = new MyObject()

当JVM遇到new时,会按照下面的流程处理

Created with Raphaël 2.2.0 开始 类加载 检查加载 分配内存 内存空间初始化 设置 对象初始化 结束
  1. 类加载,先加载对象的class
  2. 检查加载,确认class加载
  3. 分配内存,为要创建的对象划分内存区域
  4. 内存空间初始化,主要是进行零值初始化,比如int的默认值是0 boolean的默认值是false
  5. 设置,设置对象的对象头,实例数据以及对齐填充
  6. 对象初始化,调用对象的构造器

分配内存

创建对象时,要为对象分配内存空间。分配的方式有两种:

1. 指针碰撞

指针碰撞是示意图如下:
在这里插入图片描述
在内存空间规整的情况下,使用JVM使用指针碰撞的方式分配内存,指针会指向已分配内存的末端,当再分配内存时,指针会向后偏移,就像这样:
在这里插入图片描述
这种方式就被成为指针碰撞。指针碰撞的方式分配要求内存空间必须是规则且连续的

2. 空闲列表

当内存中对象的分布不连续时,JVM就会使用空闲列表法来分配对象,示意图如下:
在这里插入图片描述
JVM会对内存区域编号然后维护一个类似下面这样的列表:

区域是否占用
区域1
区域2
区域3
区域4
区域5
区域6
区域7
区域8
区域9

当需要分配内存时,JVM从空闲列表中检索可用的区域来分配内存

并发安全

当程序在运行时,不是只有一个线程需要创建对象,而是几乎所有的线程都需要创建对象,这时候为对象分配内存区域就会存在并发安全问题,假如线程1的Obj1和线程2的Obj2被分配了同一个内存区域,那程序就乱套了。JVM解决对象内存分配的并发安全问题的方式也有两种:

1. CAS

图源:King老师
图源:King老师
从上面的图中可以看出来,当JVM要为一个对象划分一块内存区域时,会使用CAS的方式,即,先读取当前内存地址A的值,经过JVM的预处理,然后将上次读取的值与现在内存地址A的值做比较,如果这两次读到的值相同,则分配成功,也就是Compare and Swap

2. TLAB

使用CAS的方式已经相对较快的(毕竟没有同步块),但是依旧需要进行一次Compare and Swap,还是有性能的损耗。所以JVM中有一个TLAB(Thread Local Allocation Buffer)机制,这是一块存在于Eden中的线程独享的内存区域,由于是线程独享的,所以并不存在并发问题,也就是快。但是TLAB的默认值是Eden的1%,如果对象比较大,还是得通过CAS的方式在堆中分配(不一定会分配在Eden中!大对象的话,会直接分配到年老代

对象的内存布局

对象的内存布局大体如下:

不一定
如果是数组
对象
对象头
实例数据
对齐填充
对象类型指针
数组长度
mark word
哈希码
对象分代年龄
锁状态标识
线程持有的锁
偏向线程id
偏向时间戳

在一个对象中,一定会有的是对象头和实例数据,但是不一定会有对齐填充。因为虚拟机规范中规范每个对象的大小都必须是8byte的整数倍,如果当前对象的实例数据+对象头的大小不是8字节的整数倍,那就需要对齐填充,将这个对象填充够8字节的整数倍。
整理一下对象的内存布局

  1. 对象头
    1. 对象运行时数据(mark word)
      1. 哈希码
      2. GC分代年龄
      3. 锁状态标识
      4. 线程持有的锁
      5. 偏向线程id
      6. 偏向时间戳
    2. 对象的类型指针
    3. 如果对象是数组类型,那还会存储数组的长度
  2. 实例数据
  3. 对齐填充(不是100%有)

对象的访问定位

对象访问定位有两种方式:

句柄池

使用句柄池的方式访问对象的意思是,栈帧中局部变量中的引用指向的句柄池中的某个句柄,然后这个句柄再指向堆中的某个对象,大概就是这样:

局部变量表中的引用
句柄A
对象1

优点:这样做的优点是局部变量表中的引用是指向句柄池中的句柄的,当对象发生变化(被回收或被移动到其他的内存地址)时,只需要更新句柄池对对象的引用即可,这样可以保证程序的稳定
缺点:由于引入了句柄池,导致了对象的访问多了一次处理从而降低了访问速度
引申:句柄池在堆里

直接指针

直接指针就是这样的:中间没有句柄池,局部变量表中的引用直接指向堆中的内存对象

局部变量表中的引用
对象1

优点:没有句柄池,也就没有中间过程,对象的访问速度提升了
缺点:实现复杂
引申:现在的Hotspot是使用直接指针的方式访问对象的

垃圾回收

众所周知,JVM的内存是自动回收的,开发人员大多数情况下不需要手动申请(malloc)、释放(free)内存。但是像C、C++这类语言是需要手动申请、释放内存的。
手动管理内存的申请释放容易产生两种问题:

  1. 忘记释放
    会导致内存泄露
  2. 反复释放
    会导致释放有用的内存对象,比如下面这样:
线程1
malloc
do something
free
do something
free
线程2
do something
do something
malloc
do something

线程1 M   F   F
线程2        M
也就是说线程1free了内存之后,线程2malloc了相同地址的内存,然后线程1反复释放了同一个地址的内存,这就会导致线程2在相同地址上创建的对象丢失

如何判断对象的是否是垃圾

引用计数法

在引用计数法中,如果没有任何引用指向一个或多个对象,那么这些对象就是垃圾的

对象上记录了自己被引用的次数,每次引用会使引用次数+1,每次置空会使引用-1

存在的问题:引用计数法无法解决循环引用的问题

引用
引用
对象1
对象2

上面这种引用关系下,对象1的引用计数为1,对象2的引用计数也是1,虽然它们与整体已经没有任何关系,但是因为彼此之间持有引用,就导致引用计数!=0最终无法被回收

可达性分析算法

JVM采用的是这种判断对象是否为垃圾的方式,JVM从GC roots开始查找对象的引用链,凡是在引用链上的对象都是活对象,其他对象为垃圾对象

GC roots
静态变量
常量
JNI
局部变量表中引用的对象
...

class的回收机制

class的回收机制非常严格,需要同时满足以下条件才能回收

  1. 堆中已经不存在当前class的任何实例
  2. classLoader已经被回收
  3. 对Class对象没有任何引用

所以说,class几乎不可能被回收,可以使用参数-Xnoclassgc来禁用class回收,这样可以稍微提高gc效率
注意: class回收是默认开启的,并不是class不能被回收,只是回收条件比较苛刻

对象的引用类型

  1. 强引用
    一般Object o1 = new Object()这样的引用就是强引用,强引用使用=
  2. 软引用
    软引用需要使用SoftReference<T>才可以,当系统即将发生OOM时,如果系统中存在软引用,那么GC将会回收软引用所引用的对象。利用这种特点,一般软引用会被用来构建一些不重要数据的内存缓存,比如一些图片、视频等,当系统内存不足时,可以被回收。
  3. 弱引用
    比软引用更弱一层的引用,需要使用WeakReference<T>,只要发生GC就一定会被回收,比如我们常用的WeakHashMap,通常也是用来构建内存敏感的缓存的。
  4. 幽灵引用

对象的分配策略

图源king老师
图源:king老师

  1. 一般来说,对象会优先分配在Eden
  2. 但是如果满足逃逸分析(对象逃不出去,且符合JTI的条件,对象可以拆解为基本数据类型)的话,会在栈上分配,栈上分配的好处是不需要进行内存回收,方法运行时,栈帧在虚拟机栈中入栈,满足逃逸分析的对象会被创建在局部变量表中,方法执行完毕后,栈帧出栈,消耗的内存自然就释放了。
  3. 如果不满足逃逸分析,对象会优先在TLAB(默认Eden的1%),如果TLAB中放不下,判断是否是对象,大对象直接进入老年代,小对象进入Eden
  4. 长期存活对象进入老年代。一般是15次,在对象头的mark word上会有分代年龄,一般是是4位的2进制数值 最大值1111,也就是15,但是也可以通过参数修改
  5. 动态年龄判断
    如果某一年龄的所有对象的大小总和已经大于S区的一半,那么这些对象会提前晋级到老年代
  6. 空间分配担保
    起因:当Eden满了之后,如果还需要分配新的对象,这时就会触发YGC,YGC的同时可能会有新的对象晋升到年老代,这时年老代需要有足够的内存空间来容纳新晋升的对象。
    实现方式:JVM计算之前每次发起YGC时晋升到年老代对象的平均大小M,如果本次晋升对象大小<=M,就可以放心发起YGC
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值