2021年9月11日

ut小美女上善若水_Java架构之道-对象的创建和内存分配

作者 adminer
大家好,我是java时间,美好的时间从学习java开始!

对象的创建

对象的创建过程如下:

类加载检查

<否则,首先必须执行相应的类加载过程。

内存分配

在类加载检查通过后,接下来虚拟机将为新的对象分配内存。对象所需存储器的大小在类别加载完成后可以完全确定,为对象分配空间的任务实际上与从Java堆中区分确定大小的存储器块相同。

存储器的分配方式有以下两种:

指针碰撞假设堆中的存储器是绝对规则的,所有的存储器都放在一边,空闲的存储器放在另一边,中间放置指针作为分界点的指示器

空闲列表的内存不规则,已经使用的内存和空闲的内存交错,无法简单地指针碰撞,虚拟机必须维持列表,记录哪个内存是可用的

同步处理分配内存空间的动作-实际上虚拟机是CAS和失败再试的方法,保证更新操作的原子性。

将存储器分配的动作按线程分配到不同的空间,即每个线程在Java堆中预先分配一小块存储器,称为当地线程分配缓冲器(TLAB)

选择哪种分配方式由Java堆是否规则决定,Java堆是否规则,由采用的垃圾收集器是否具有空间压缩整理的能力决定。

因此,当使用带压缩整理过程的收集器时,系统采用的分配算法是指针碰撞,即简单高效。使用CMS这种基于清除算法的收集器时,理论上只能使用复杂高效的空闲列表来分配内存。

初始化

内存分配完成后,虚拟机需要将分配的内存空间初始化为零(不包括对象)这一步的操作保证了受众的实例字段在Java代码中可以直接使用,而且程序可以访问这些字段的数据类型对应的零值。

设置对象头

HotSpot虚拟机,对象存储在内存中的布局分为三个区域:对象

对象头包括markWord和Class对象指针:

其中markWord和Classty指针:

其中malitig”mextif”mentit”mentit”mentit”me”mentit”ment”metititit”,mantitititititit”,其中martit”me”martit”me”me”me”me”me”mentit-cle”me”的对于存储对于存储对象头部分的数据,包括:clititexplititititititititititititititite”的对象,包括:“hexplitentititititititititititite”的对象(hexplexplitit:”的方法:clententintitexplexplentititentititititit:”的表示:”的对象(hexplexplentititititititentent但是在Java程序的视角看来,初始化才正式开始,开始调用<init>方法完成初始复制和构造函数,所有的字段都为零值。因此一般来说(由字节码中是否跟随有invokespecial指令所决定),new指令之后会接着就是执 行<init>方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全创建出来。

对象存储器分配

对象存储器的分配过程如下:

(1)首先判断对象是否可以在栈上分配

比如下图中,test1会返回user对象,当方法返回时,user对象可能会被外部访问,因此不能在栈上分配,text2中创建的user对象会在方法调用完毕后立即被销毁,因此可以在栈上分配

 Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
 Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上

大量的对象被分配在eden区,eden区满了后会触发minor gc,大多数对象会成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块survivor区,下一次eden区满了后又会触发minor gc,把eden区和survivor区垃圾对象回 收,把剩余存活的对象一次性挪动到另外一块为空的survivor区,因为新生代的对象都是朝生夕死的,存活时间很短,所 以JVM默认的8:1:1的比例是很合适的,让eden区尽量的大,台湾UT视讯聊天室survivor区够用即可。

JVM默认有这个参数-XX: UseAdaptiveSizePolicy(默认开启),会导致这个8:1:1比例自动变化,如果不想这个比例有变 化可以设置参数-XX:-UseAdaptiveSizePolicy

(3)大对象直接进入老年代

大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。通过JVM参数-XX:PretenureSizeThreshold可以设置大对象的标准,如果对象超过设置大小直接进入老年代,就不会进入年轻代。需要注意的是,这个参数只能在Serial和ParNew下生效。

JVM为什么要这样设置?

为了避免大对象在新一代来回复制,垃圾回收效率下降

(4)长期生存的对象进入老年人

JVM既然采用了代替收集的思想来管理存储器,就必须知道存储器回收时应该放在新的对象中。为了实现这一点,ut小美女上善若水ut小美女上善若水虚拟机给每个目标年龄(Age)计数器。

如果对象出生在Eden,经过第一次Minor-GC后仍然可以生存,并且可以容纳在Survivor中,将对象年龄设定为1。对象在Survivor中每次度过MinorGC,年龄增加1岁,年龄增加到一定程度时(默认为15岁,CMS收集器默认为6岁,垃圾收集器稍有不同),升级为老年人。对象升级到老年代的年龄门槛,可以通过参数-XX:MaxTenuringThreshold设置。

(5)对象的动态年龄判断

现在放置对象的Survivor区域(其中一个区域,放置对象的区域),一个对象的总大小比这个Survivor区域内存大小的50%(-XX:TargetSurvivivorartior可以指定),这个时候大于这个对象年龄最大的对象以直接进入Survivivivor区域,ut小美女上善若水例如超过1年龄。
rvivor区域的50%,此时将年龄n(包括)以上的对象放入老年代。这个规则其实就是希望那些可能是长生存的对象,尽快进入老年人。对象的动态年龄判断机制一般是在minorgc之后触发的

为什么要保证空间?

新一代采用复制收集算法,如果大量对象在Minor下GC后仍然存活(最极端的情况是内存回收后,新一代的所有对象都存活),Survivor的空间比较小的话,老年人需要分配保证,ut小美女上善若水将Survivor不能容纳的对象存放在老年人身上。老年人必须保证空间分配。老年人必须有足够的空间来容纳这些对象,但是有多少对象在内存回收后生存是不可预测的,所以必须参考每次垃圾回收后升级到老年人对象大小的平均值。使用这个平均值与老年人的剩馀空间进行比较,决定是否进行FullGC,让老年人腾出更多的空间。

更多精彩内容请关注公众号:java时光