java对象内存分配
对象一定是分配到堆上吗?
不是的
java对象的内存分配流程 – 前度刘郎 – 博客园 (cnblogs.com)
https://img2020.cnblogs.com/blog/1187916/202110/1187916-20211012101818062-1048005036.png

第一步、满足一定条件,可以在栈上分配对象

Test2()方法的user对象只会在当前方法内有效,如果放在堆里,在方法结束后,其实这个对象就已经是垃圾的,但却在堆里占用堆内存空间。如果将这个对象放入栈中,随着方法入栈,逻辑处理结束,对象就变成垃圾了,再随着栈帧出栈。这样可以节约堆空间。尤其是这种非逃逸对象很多的时候。可以节省大量的堆空间,降低GC的次数。
1.开启逃逸分析,2开启标量替换。默认开启
逃逸分析,如果一个类的方法都没有返回值,他肯定不会被外部访问(可能不准确,意思是这个意思)。判断一个对象是否是逃逸对象,就看这个对象能否被外部对象访问到。
标量替换,栈内没有连续空间放对象,那么会把对象的属性放到不同的空闲空间上。
这样的对象,就会在栈上直接分配了。
第二步:判断是否是大对象,不是放到Eden区
判断是否是大对象,如果是则直接放入到老年代中。
如果不是,则判断是否是TLAB?如果是则在Eden去分配一小块空间给线程,把这个对象放在Eden区。如果不采用TLAB,则直接放到Eden区。
什么是TLAB呢?本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。简单说,TLAB是为了避免多线程争抢内存,在每个线程初始化的时候,就在堆空间中为线程分配一块专属的内存。自己线程的对象就往自己专属的那块内存存放就可以了。这样多个线程之间就不会去哄抢同一块内存了。jdk8默认使用的就是TLAB的方式分配内存。
通过-XX:+UseTLAB参数来设定虚拟机是否启用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。
第三步 是大对象 放入到老年代
1.什么是大对象?
- Eden园区放不下了肯定是大对象。
- 通过参数设置什么是大对象。-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC。如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。
- 长期存活的对象将进入老年代。虚拟机采用分代收集的思想来管理内存,虚拟机给每个对象设置了一个对象年龄(Age)计数器。 如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。
2.为什么要将大对象直接放入到老年代呢?
为了避免为大对象分配内存时的复制操作而降低效率。
3.什么情况要手动设置分代年龄呢?
如果我的系统里80%的对象都是有用的对象,那么经过15次GC后会在Survivor中来回翻转,这时候不如就将分代年龄设置为5或者8,这样减少在Survivor中来回翻转的次数,直接放入到老年代,节省了年轻代的空间。