JVM对象创建与内存分配机制
2022-10-07 13:49:20 0 举报
AI智能生成
JVM对象创建与内存分配机制
作者其他创作
大纲/内容
JVM对象创建过程解析
new一个对象,总体会经历类加载检查、分配内存、初始化零值、设置对象头、执行clinit5个过程,一个对象的大小是多少
类加载检查
检查类是否已加载,如果未加载便去加载
分配内存
给类对象分配内存空间,如何分配?分配时并发如何解决?
如何分配
指针碰撞
当空间规整(比如整理-复制算法清理出来的内存),分配内存指针指向之后的内存都是未分配内存
默认使用该方式
默认使用该方式
空闲列表
空间不规整(整理-清除算法),维护一个列表,记录可用空间
分配时并发如何解决
CAS+重试
本地线程分配缓冲TLAB
线程初始化时,给每个线程在堆上提前分配一块空间,用于该线程创建对象
通过XX:+/ -UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启XX:+UseTLAB),XX:TLABSize 指定TLAB大小
如果分配的TLAB大小不够对象存储,会使用CAS+重试
如果分配的TLAB大小不够对象存储,会使用CAS+重试
初始化零值
给分配的空间赋零值(对象头之后设置),若使用TLAB分配,这个步骤可以放在TLAB分配的时候完成
设置对象头
对象由对象头+实例数据+对齐填充三块构成
对象头=markword + Kclass指针+数组长度(数组才有)
markword:自身运行时数据
记录对象哈希值、锁状态、分代年龄等信息,32位占4字节,64位上占8字节
当对象拥有轻量级锁时,它的hashcode、分代年龄这些信息会被赋值到栈上存储,这边只存储指针
当对象拥有轻量级锁时,它的hashcode、分代年龄这些信息会被赋值到栈上存储,这边只存储指针
Kclass指针
是JVM虚拟机找寻类元信息入口,64位机器上,开启指针压缩占4字节,不开启指针压缩8字节
注意:加载时生成的Class类对象,是程序员找到类元信息的入口
注意:加载时生成的Class类对象,是程序员找到类元信息的入口
指针压缩
默认开启,一般机器上内存为8G、16G或者32G,如果需要寻址,最多需要35位,指针压缩就是采用压缩算法,将需要33-35位才能寻出来的地址,压缩成32位,实际寻址的时候再解码成33-35位的,超过35位的直接失效,所以一般建议堆内存不要超过32G
//-XX:+UseCompressedOops 默认开启指针压缩所有指针,这个所有的都有效,包括对象指针
//-XX:+UseCompressedClassPointers 默认开启的只压缩对象头里的类型指针Klass Pointer
//-XX:+UseCompressedClassPointers 默认开启的只压缩对象头里的类型指针Klass Pointer
开启指针压缩的原因
节约内存,使用较大指针在主内存和缓存间移动数据,会占用较大带宽,且对GC压力也较大
数组长度
4字节,只有数组才有
实例数据
数据的排列可能是打乱的,会有对齐
对齐填充:8的倍数
执行clinit
执行赋值操作,然后执行构造函数
JVM对象内存分配
对象内存分配流程图
对象栈上分配
栈上分配
在栈上分配空间存储对象
原因:出栈时即销毁,无需垃圾清理
逃逸分析
分析对象是否会逃出当前所在作用域,如果不会,则尝试在栈上分配,栈上分配时若整体空间不够,则会标量替换
标量替换
将对象内的属性拆分,放在内存的不同位置
对象在Eden分配
大多数情况下,对象在新生代中Eden去分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC
MinorGC:发生在新生代的垃圾收集动作,MinorGC非常频繁,回收速度一般也较快
FullGC:一般会回收老年代、年轻代、方法区的垃圾,MajorGC的速度一般会比MinorGC的慢10倍以上
MinorGC:发生在新生代的垃圾收集动作,MinorGC非常频繁,回收速度一般也较快
FullGC:一般会回收老年代、年轻代、方法区的垃圾,MajorGC的速度一般会比MinorGC的慢10倍以上
大对象直接进入老年代
当对象所需内存大于阈值时,直接进入老年代,只在Serial和Parlnew垃圾收集器生效,可设置
为什么:减少大对象在Eden区和Survivor区复制的消耗
长期存活的对象将进入老年代
超过分代年龄的对象会迁移到老年代
对象动态年龄判断
S1区中,其中分代年龄1+···+分代年龄n的对象内存之和大于S1区内存的50%,此时就会把分带年龄n及其之上的对象就会被挪动到老年代
动态年龄判断机制一般是在MinorGC之后触发的
动态年龄判断机制一般是在MinorGC之后触发的
老年代空间分配担保机制
MinorGC之前JVM都会计算老年代剩余可用空间
JVM参数设置案例
原则:尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年大,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁进行垃圾回收
场景:秒杀,每秒1000多单,三台后台服务器(4核8G),每台每秒处理300多单,每个订单对象假定为1KB,每秒300KB订单对象生成,因下单还涉及其他对象(库存、优惠券、积分等),放大20倍,则6000KB每秒(6MB),同时还有其它操作,再放大十倍,则每秒有60MB对象生成,1秒后都成为垃圾对象
原本JVM设置:java -Xms3072M -Xmx3072M -Xss1M -XX:MetaspaceSize=521M -XX:MaxMetaspaceSize=521M -jar microservice-eureka-server.jar
会经常产生FullGC,为什么呢?
原本JVM设置:java -Xms3072M -Xmx3072M -Xss1M -XX:MetaspaceSize=521M -XX:MaxMetaspaceSize=521M -jar microservice-eureka-server.jar
会经常产生FullGC,为什么呢?
如何优化呢?将Survivor区域变大,或者将整个新生代放大
java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=521M -XX:MaxMetaspaceSize=521M -jar microservice-eureka-server.jar
java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=521M -XX:MaxMetaspaceSize=521M -jar microservice-eureka-server.jar
对象内存回收
针对堆
主要是判断对象死亡,并回收其内存空间
判断对象死亡
引用计数
存在循环引用问题
可达性分析
常见引用类型
强引用
软引用
正常情况不会回收,但是GC做完发现释放不出空间存放新对象,则会把这些对象回收
软引用可用来实现内存敏感的高速缓存
SoftReference
软引用可用来实现内存敏感的高速缓存
SoftReference
弱引用
WeakReference
GC会直接回收掉
GC会直接回收掉
虚引用
finalize()方法最终判定对象是否存活
在可达性分析中标记为不可达的对象,也并非“非死不可”的,这时候他们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链
第一次标记:
筛选的条件是此对象是否有必要执行finalize()方法
当对象没有覆盖finalize方法,对象将直接被回收
第二次标记:
如果对象覆盖了finalize方法,可以在finalize重新建立连接,实现自救
注意:一个对象的finalize方法只会被执行一次
第一次标记:
筛选的条件是此对象是否有必要执行finalize()方法
当对象没有覆盖finalize方法,对象将直接被回收
第二次标记:
如果对象覆盖了finalize方法,可以在finalize重新建立连接,实现自救
注意:一个对象的finalize方法只会被执行一次
针对方法区
判断类信息不再使用,需要满足三个条件
该类所有的实例对象都已经被回收,也就是Java堆中不存在该类的任何实例
加载该类的ClassLoader已经被回收
该类对应的java.lang.Class对象没有在任何地方引用,无法在任何地方通过反射访问该类的方法
0 条评论
下一页