java技术体系中所提倡的自动内存管理最后能够归结为自动化解决两个问题:给对象分配内存以及回收分配给对象的内存,关于回收内存,上一篇已经说了不少JVM内部的垃圾收集器体系以及他运行的垃圾收集算法,如今咱们在探讨一下给对象分配内存的时候须要注意的事项。java
对象的内存分配,大的讲,就是在java heap上分配,对象主要分配在新生代的Eden空间上,若是启用了本地线程缓冲分配,那么将按照线程优先在TLAB上分配,少数状况也可能直接分配在老年代。可是分配的原则不是百分之百固定的,还要取决你采用的那种垃圾收集器的二组合,以及虚拟机中内存设置的相关参数。
算法
1:对象优先在Eden上分配
数组
大多数状况下,对象在新生代Eden区域中进行分配,档Eden区域中没有足够的空间进行分配的时候,发起一次Minor GC
spa
注意提到的Minor GC和Full GC
线程
Minor GC:这就是新生代的GC,指的是新生的垃圾收集动做,因为java对象大多具备朝生夕死的特性,因此Minor GC很是频繁,通常回收速度也比较快。
对象
Full GC:指的是发生在老年代的GC,又被称做Major GC,出现了Major GC,常常会伴随这至少一次的Minor GC,但这也不是绝对的,通常来讲Major GC的速度会比Minor GC慢十倍以上。内存
2:大对象直接进入老年代资源
所谓的大对象是指大量须要连续java内存空间的java对象,最典型的大对象就是那种很长的字符串或者数组。大对象对于虚拟机分配来讲是个很坏的消息,可是也有更坏的,就是那种朝生夕死的大对象,常常会出现大对象容易致使内存还有很多空间的时候就提早出发垃圾收集机制以获取足够的连续空间来“安置”他们。字符串
虚拟机提供了一个参数,上一篇也讲到过,就是-XX:PretenureSizeThreshold参数,让大于这个参数的对象直接进入老年代分配,这样的目的就是避免在Eden区以及两个Survivor区之间发生大量的内存复制,这是由于新生代采用的复制算法,复制大量的生存对象会耗费不少资源。
虚拟机
3:长期存活的对象将进入老年代
虚拟机既然采用分代收集的思想来管理内存,那内存回收的时候就必须能识别那些对象放在新生代,那些对象放在老年代,为了作到这点,虚拟机给每一个对象定义了一个对象年龄计数器,若是对象在Eden出生而且通过一次Minor GC后仍然存活,而且能被Survivor接纳,年龄就加一岁,当他的年龄增长到必定程度的时候,默认是15,就会晋升到老年代,这个值能够经过虚拟机的参数-XX:MaxTenuringThreshold来进行设置。
4:动态对象年龄断定
为了能更好的适应不一样程序的内存状况,虚拟机并不老是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,若是在Survivor空间中相同年龄全部对象大小综合大于Survivor空间的一半,年龄大于等于该年龄的对象就能够直接进入老年代,无语等到MaxTenuringThreshold中要求的年龄。
5:空间分配担保:
这点就给借钱时候的保人相似,若是到时间你换不上,那么保人要先把钱换上。
言归正传,在发生Minor GC的时候,虚拟机会检测以前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,若是大于,则改成直接进行一次Full GC,若是小于,则查看HandlePromotionFailure设置是否容许担保失败;若是容许,那只会进行Minor GC,若是不容许,则也要进行一次Full GC.
因为新生代采用复制收集算法,可是为了保证内存的利用率,只是用其中一块Survivor空间做为轮换备份,所以当出现大量对象在Minor GC后任然存活的状况下,就须要老年代进行分配担保,让Survivor没法容纳的对象直接进入老年代,与生活中贷款很相似,老年代要作这样的担保,前提是老年代自己还能够容纳这些对象,可是实际中通过Minor GC以后新生代到底会有多少对象存活下来,这个是不晓得的,因此只好去以前每一次回收晋升到老年代对象大小的一个平均值做为经验值,与老年代剩余的空间大小作比较,决定是否进行Full GC来让老年代腾出更多的空间。