
对象创建过程(对象头)、内存分配详解
⼀、对象创建流程分析
1、类加载检查
jvm遇到⼀条new指令时,⾸先将去检查这个指令的参数是否能在常量池中定位到⼀个类的符号引⽤,并且检查这个符号引⽤代表的类是否
已被加载、解析和初始化过。如果没有,那必须先执⾏相应的类加载过程。
new指令对应到语⾔层⾯上讲是,new关键词、对象克隆、对象序列化等 2分配内存
类加载完成后,所占⽤Eden内存⼤⼩基本确定(类的属性所占⼤⼩等)
分配内存⽅法:-指针碰撞(默认指针碰关于爱的格言 撞)
如果堆中的内存是规整的(即所有⽤过的内存在⼀边,没⽤过的内存在另⼀边,中间有⼀个指正分割。分配内存只需移动指正即可)-空闲列表
如果堆中的内存不是规整的,即⽤过的内存和没⽤过的内存是打乱交叉,此时没法⽤指正碰撞。此时jvm会维护⼀个空闲列表,记录那些内存是可⽤的,分配内存时,会在空闲列表找到⼀个⾜够⼤的内存为这个对象分配,然后更新空闲列表。
上述分配内存单线程没问题,当并发多线程来争抢指针/空闲列表就会存在问题
jvm提供内存分配解决并发问题
C消除粉刺最有效的方法 as(Compareandsweep)
jvm采⽤CAS配上失败重试的⽅式保证更新操作的原⼦性来对分配内存空间的动作进⾏同步处理本地线程分配缓冲(Threadlocalallocationbuffer/TLAB)
jvm为每个线程在堆中分配⼀部分内存,这个线程产⽣对象都分配在这块内存上
JVM会默认开启XX:+UTLAB,XX:TLABSize指定TLAB⼤⼩
3、初始化
内存分配完成后,对对象初始化零值4设置对象头
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对幼儿园消防培训 象头(Header)、实例数据(InstanceData)和对齐填充
(Padding)。HotSpot虚拟机的对象头包括两部分信息,第⼀部分⽤于存储对象⾃⾝的运⾏时数据,如哈希码(HashCode)、GC分
代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。对象头的另外⼀部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
下⾯具体看下对象头数据 pom⽂件需引⼊
publicclassJolTest{
publicstaticvoidmain(String[]args){
ClassLayoutlayout=nstance(newObject());
n(table());
n();
ClassLayoutlayout1=nstance(newString[]{});
n(table());
}}
注意上图klasspointer类型指针都是4个字节,正常是8个字节,消防演讲 都存在指针压缩。
指针压缩(jvm默认都是开启的) 优点:
在64位平台的HotSpot中使⽤32位指针,内存使⽤会多出1.5倍左右,使⽤较⼤指针在主内存和缓存之间移动数据 注意事项:
(1)堆内存⼩于4G时,不需要启⽤指针压缩,jvm会直接去除⾼32位地址,即使⽤低虚拟地址空间
(2)堆内存⼤于32G时,压缩指针会失效,会强制使⽤64位(即8字节)来对java对象寻址,所以堆内存不要⼤于32G为好(占⽤较
⼤宽带,同时GC也会承受较⼤压⼒)5、初始化
对对象赋值(真正意义的值)、执⾏构造⽅法8的英语怎么写
⼆、对象内存分配
1、对象在年轻代Eden区分配 Eden与Survivor区默认8:1:1
⼤量的对象被分配在eden区,eden区满了后会触发minorgc,⼤部分对象成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块
survivor区,下⼀次eden区满了后⼜会触发minorgc,把eden区和survi国际法案例 vor区垃圾对象回收,把剩余存活的对象⼀次性挪动到另外⼀块为
空的survivor区,因为新⽣代的对象都是朝⽣⼣死的,存活时间很短,所以JVM默认的8:1:1的⽐例是很合适的,让eden区尽量的
⼤,survivor区够⽤即可2、对象分配在栈上
正常对象都分配在堆上,jvm默认开启逃逸分析,⽅法内部没有逃逸的对象会分配在栈上 没有逃逸的对象:
就是⽅法内部创建的对象没有被外部引⽤,如下。
publicvoidtest(){
byte[]b=newbyte[10];}
优点:
因为分配在栈上,⽅法执⾏完就会释放内存,负责分配在堆上得等到gc时才能收集,占⽤内存,要是⼤对象可能直接分配到⽼年代,可能会触发fullgc
下⾯demo,开启逃逸分析和没有开启标量替换(**必须两个都开启才能最⼤程度避免gc**)-Xmx15m-Xms15m-XX:+DoEscapeAnalysis-XX:+PrintGC-XX:-EliminateAllocations
publicclassAllotOnStack{
publicstaticvoidmain(String[]args){
longstart=tTimeMillis();
for(inti=0;i<100000000;i++){
alloc();
}
longend=tTimeMillis();
n(end-start);
}
publicstaticvoidalloc(){
Urur=newUr();
e("1231");
}}
3、⼤对象直接分配到⽼年代
JVM参数-XX:PretenureSizeThreshold可以设置⼤
对象的⼤⼩,如果对象超过设置⼤⼩会直接进⼊⽼年代,不会进⼊年轻代,这个参数只在Serial和ParNew两个收集器下有效。避免⼤对象
在gc在年轻带复制来复制去,占⽤带宽4、对象动态年龄判断
当前放对象的Survivor区域⾥(其中⼀块区域,放对象的那块s区),⼀批对象的总⼤⼩⼤于这块Survivor区域内存⼤⼩的50%(-
XX:TargetSurvivorRatio可以指定),那么此时⼤于等于这批对象年龄最⼤值的对象,就可以直接进⼊⽼年代了,例如Survivor区域⾥现在有⼀批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会
把年龄n(含)以上的对象都放⼊⽼年代。这个规则其实是希望那些可能是长期存活的对象,尽早进⼊⽼年代。对象动态年龄判断机制⼀般是在minorgc之后触发的

本文发布于:2023-04-12 04:56:20,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/zhishi/a/168124658043696.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:分配对象.doc
本文 PDF 下载地址:分配对象.pdf
| 留言与评论(共有 0 条评论) |