JVM对象创建与内存分配机制

发布于:2025-07-22 ⋅ 阅读:(17) ⋅ 点赞:(0)

对象创建过程:

一:是否加载

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等

二:分配内存

如何分配?

指针碰撞(默认)

如果内存是规整了,所有用过的内存在一边,空闲的在另一边,中间放着一个指针作为分界点指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。

空闲列表

如果内存不规整,就必须维护一个列表,记录哪些内存可用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

并发情况怎么处理?

cas 

TLAB(Thread Local Allocation Buffer)本地线程分配缓存,8版本默认开启,JVM参数可以控制 -XX:+/UseTLAB大,-XX:TLABSize 小为伊甸园区的1%如果放不下,就走cas

三: 初始化

四:设置对象头

对象填充的作用? 保证对象是8字节的倍数,对象寻址效率最高的方式,计算机原理定义的。

对象指针表示的是?-XX:+UseCompressedOops(默认开启)

对象指针用到了指针压缩(小于等于32G),那么什么是指针压缩? 节约内存空间 , 32位的操作系统只能表示4g内存,我们平时工作的都是8G以上的也就是2的33次方或者更多,指针压缩就是通过算法,把超出来的变成32的,通过他找对象的时候,又变成实际的来找,节约内存资源,因此堆内存尽量建议不要超过32G. 否则会用8位来存储寻址,导致带宽加大,同时GC也会有较大压力

堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现1的问题,所以
堆内存不要大于32G为好

五:执行init方法

给对象赋值和调用构造方法

整个分配过程涉及到的知识点:

对象逃逸分析?

减少gc压力,把不逃逸的分配到栈的栈帧里(默认128k?兆) 例如void方法

jdk7默认开始,-XX:+DoEscapeAnalysis(+开启-关闭)

标量替换? 

通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启

标量即不可被进一步分解的量,而JAVA的基本数据类型就是标量(如:int,long等基本数据类型以及reference类型等),标量的对立就是可以被进一步分解的量,而这种量称之为聚合量。而在JAVA中对象就是可以被进一步分解的聚合量。

栈上分配依赖: 逃逸分析和标量替换

对象在内存中的分配流程?

大对象直接进入老年代, 防止在幸存者区来回复制移动

长期存活的对象进入老年代 --XX:MaxTenuringThreshold参数来设置多大年龄进入

伊甸园区的比例是否开启(8:1:1),开启参数,默认为自适应

对象动态年龄判断机制: 会导致1秒就称为垃圾的系统存入老年代

大对象一般怎么处理?

JVM参数 -XX:PretenureSizeThreshold (单位字节)可以设置对象的大小,超过这个设置的会直接进入老年代,这个参数只在Serial和ParNew两个收集器下有效。

比如设置JVM参数:-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC 代码执行会发现大对象直接进了老年代

这样做的目的:减少为大对象分配内存时的复制操作而降低效率。

长期存活对象进入老年代:

-XX:MaxTenuringThreshold可以设置值,默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同。不超过15的原因是记录年龄的这块分配了4bit位置

对象动态年龄判断?

当前放对象的Survivor区域里,一批对象的总大小大于Survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就会直接进入老年代里。会引起GC。 对象动态年龄判断机制一般是在minor gc之后触发的。
这个规则其实是希望哪些尽可能长的对象尽早的进入老年代

老年代空间分配担保机制?

-XX:-HandlePromotionFailure 在java8默认开启了
在minor gc前判断 老年代剩余空间是否大于等于 年轻代里所有的对象(包含垃圾) ;
如果大于就minor gc回收;如果不足继续:
如果没开启,直接Fullgc 然后在 minor gc 
如果开启了这个机制,会再次判断,剩余空间是否>=历届minor gc回收后剩余的值, 如果满足,minor gc 否则直接Fullgc 然后在 minor gc ;
总的来说是一种优化。

为什么元空间不怎么能回收到垃圾(无用类)?

条件苛刻,基本上只有热部署的像tomcat那种的才会被回收,需要满足以下条件:

1.类所有的实例已经被回收,即java堆中没有该类的任何实例。

2.加载该类的ClassLoader已经被回收

3.该类对应的java.lang.Class对象已经没有任何地方饮用了,无法在任何地方通过反射访问该方法。


网站公告

今日签到

点亮在社区的每一天
去签到