内存分配策略算法
了解GC其中很重要一点就是了解JVM的内存分配策略:即对象在哪里分配和对象何时回收。数组
Java技术体系中所提倡的自动内存管理能够归结于两个部分:给对象分配内存以及回收分配给对象的内存。
咱们都知道,Java对象分配,都是在Java堆上进行分配的,虽然存在JIT编译后被拆分为标量类型并简介地在栈上进行分配。若是采用分代算法,那么新生的对象是分配在新生代的Eden区上的。若是启动了本地线程分配缓冲,将按线程优先在TLAB上进行分配。
事实上,Java的分配规则不是百分百固定的,其取决于当前使用的是哪种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。 安全
简单来讲,对象内存分配主要是在堆中分配。可是分配的规则并非固定的,取决于使用的收集器组合以及JVM内存相关参数的设定。
下面Serial和Serial Old收集器作一个内存分配和回收的策略总结。线程
1.对象优先在新生代Eden分配对象
首先,让咱们来看一下新生代的内存分配状况:
内存分配状况:
将JVM内存划分为一块较大的Eden空间(80%)和两块小的Servivor(各占10%)。当回收时,将Eden和Survivor中还存活的对象一次性采用复制算法直接复制到另一块Servivor空间上,最后清理到院Eden空间和原先的Survivor空间中的数据。
大多数状况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,JVM将发起一次Minor GC。
在这里先说明两个概念:blog
· 新生代GC(Minor GC):指发生在新生代的垃圾收集动做,由于Java对象大可能是具备朝生夕灭的特性,因此Minor GC很是频繁,并且该速度也比较快。内存
· 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,通常可能也会伴随着一次Minor GC,可是与Minor GC不一样的是,Major GC的速度慢十倍以上。资源
2.大对象直接进入老年代字符串
咱们先对所谓的大对象作一个定义:大对象,这里指的是须要大量连续内存空间的Java对象。最典型的大对象能够是很长的字符串和数组。
JVM对大对象的态度:
大对象对于JVM的内存分配来讲是十分麻烦的,若是咱们将大对象分配在新生代中,那样子的话很容易致使内存还有很多空间时就提早触发垃圾收集以获取足够的连续空间来“安置”它们。、
为了不上述状况的常常发生而致使不须要的GC活动所浪费的资源和时间,可采用的分配策略是将大对象直接分配到老年代中去,虚拟机中也提供了-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代里面分配内容。虚拟机
-XX:PretenureSizeThreshold只对Serial和ParNew收集器有效。
3.长期存活的对象将进入老年代
当JVM采用分代收集的思想来管理内存时,为了识别哪些对象应该放在新生代、哪些对象应该放在老年代,JVM给每一个对象定义了一个对象年龄计数器。
对象年龄计数器:若是对象在Eden出生并通过第一次Minor GC后仍然存活,而且能被Survivor容纳的话,即可以被移动到Survivor空间中,年龄计数器将设置该对象的年龄为1.对于对象在Survivor区每通过一次Minor GC,年龄便增长1岁,当它的年龄增长到必定程度(可经过参数-XX:MaxTenuringThreshold设置)默认15,该对象便会进入到老年代中。成为老年代的对象。
4.动态对象年龄断定
事实上,有的虚拟机并不永远地要求对象的年龄必须达到MaxTeruringThreshold才能晋升老年代,若是在Survivor空间中相同年龄全部对象大小的总和大于Surivior空间的一半,年龄大于或等于该年龄的对象就能够直接进行老年代,无须等到MaxTeruringThreshold中所要求的年龄。
5.空间分配担保
在发生Minor GC以前,虚拟机会先检查老年代中最大的可用的连续空间是否大于新生代中全部对象总空间,若是这个条件成立,那么Minor GC能够确保是安全的,若是不成立,则虚拟机会查看HandlePromotionFaiure设置值是否容许担保失败。若是容许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,若是大于,将尝试进行一次Minor GC,尽管此次GC是有风险的;若是小于,或者HandlePromotionFaiure设置不容许冒险,那么这时就要改成进行一次Full GC。 所谓冒险:也就是说当用来轮转的Survivor区没法承受新生代中所存活的对象内存时,须要老年代进行分配担保,把Survivor没法容纳的对象直接进入老年代中,前提是老年代中。