起源
最近面试,基本上都有被问到jvm内存分配与垃圾回收。
遂整理书籍内容,结合一些文章并融合自身理解。
阅读本文,假定你知道新生代,老年代以及一些专有名字。若不懂则自行百度
总体jvm分配流程(个人理解)
所有对象 直接分别在新生代, 对象优先分配在Eden区 即【新生代】。
若大于设定得大对象定义得阈值则直接分配到老年代中,若不大于则分配到新生代中去。
若空间不足,则促发MinorGC/YoungGC ——新生代GC。由于在新生代的GC过程中,会有从新生代晋升到老年代的情况。在执行新生代GC之前,会去老年代中检查一下平均每次晋升到老年代对象的平均值,目前老年代是否有足够的空间分配平均值。如果空间足够,则说明执行新生代GC是安全的;
若不够则判断有没有设置 空间分配担保,设置为允许则表示认为这次的新生代GC产生的老年代将非常小。如果是的话,就节省了一次FullGC 老年代GC的时间,尽管这次GC是有风险的;设置为不允许则需要执行一次FullGC,再接着执行新生代GC才是安全的。
那么再接下来就是执行新生代GC,过程中会涉及到晋升到老年代的情况。
针对新生代GC中还有2个点,就是设置晋升到老年代的年龄;以及动态对象年龄判定,即有些时候并不是非得要达到设置的年龄才会晋升到老年代。
看完下面的,再回过头来看上边的描述。就能非常清晰的知道jvm分配内存的过程了。
对象优先分配再Eden区
在大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。
要外之意就是,如果不做特别变态的配置。理应所有对象都是从 基层 慢慢 变到 高层的。就像大多数人都是普通人一样,只有少数人靠着一些外界的辅助非常快速的走上了 上流社会。
例如:段子太多我就不多说了,我要开着我女朋友80大寿送的兰博出去透透气了。这2w平米的私人豪宅住着太闷了
大对象直接进入老年代
大对象就是需要大量连续空间的Java对象,最典型的大对象就是那种很长的字符串及数组。虚拟机提供了一个 -XX:PretenureSizeThreshold 参数,令大于这些设置值的对象直接在老年代中分配。
若是分配在Eden区,倘若大对象恰好是Eden区的空间大小。则需要进行很多次的GC之后才能保证有足够的空间进行分配,且其间会有其他的对象申请空间。好比与我们的食堂,大部分人吃2碗饭。突然来了一个大胃王,要吃整整666kg的米饭。把这个666kg的米饭给放到器皿中,将会是一个非常慢的操作。
通常的操作,1你要么等着这些吃2碗的同学都吃完了,然后慢慢给你操作;这是非常低效的。
要么就是有一个专门供大佬吃饭的地方,你自己慢慢操作。
这就类比于大对象直接进入老年代
长期存活的对象将进入老年代
这个长期存活进入老年代的就不过多解释了,本来就是一个正常操作。相信大家都能理解。
动态对象年龄判定
在某种情况下。Eden区空间不够了,但是很多存活对象又不够年龄,不能晋升到老年代。目前又需要有足够的空间需要马上进行分配给迫切的对象。
倘若jvm是个死脑筋,那么jvm将进行很多次的YoungGC之后才能,满足这个迫切的对象空间申请。
针对这种情况,jvm是可以改变策略的。即动态对象年龄判定为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
这个在生活中就有很多例子,大家自行体会吧。
空间分配担保
这个就是在发生YoungGC时,有部分对象需要晋升到老年代中。
最最保险的是要老年代空间有充足的剩余空间 装下目前所有可能要晋升到老年代的空间。
如果按照这种最最保险的操作进行的话,就会出现:为了保证老年代空间有足够的空间装下所有可能晋升的对象,肯定会触发FullGC即老年代GC。这个FullGC要比YoungGC的耗费时间和资源是更多更长的。就会导致程序慢,效率不高。因为不是所有的对象都能到达老年代
所以有一个配置【-XX:+/- HandlePromotionFailure】用于,在老年代空间不足的情况下。是否需要执行FullGC
书上说在发生Minor GC【就是YoungGC】之前,jvm会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。
如果不成立,则jvm会查看HandlePromotionFailure设置值是否允许担保失败。如果允许会继续检查老年代最大可用的连续空间首付大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次MinorGC是有风险的;如果小于,或者HandlePromotionFailure设置允许冒险,那这是也要改为进行一次FullGC。