堆内存划分为 新生代(Eden空间、Survivor空间)和 老年代(Tenured/Old 空间)。
安全
大可能是状况下,对象在新生代Eden区中分配。当Eden区中没有足够空间进行分配时,虚拟机将发起一次Minor GC
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 打印gc日志
-Xms20M -Xmx20M 指定堆内存
-Xmn10M 指定新生代内存
-XX:SurvivorRatio=8 指定Eden区与一个Survivor区的空间比例是8:1。
并发
private static final int _1MB = 1024 * 1024; public static void main(String[] args) { byte[] allocation1 = new byte[2 * _1MB]; byte[] allocation2 = new byte[2 * _1MB]; byte[] allocation3 = new byte[2 * _1MB]; byte[] allocation4 = new byte[4 * _1MB]; }
[GC (Allocation Failure) [PSYoungGen: 6420K->1010K(9216K)] 6420K->3594K(19456K), 0.0157046 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] Heap PSYoungGen total 9216K, used 5427K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 53% used [0x00000000ff600000,0x00000000ffa50630,0x00000000ffe00000) from space 1024K, 98% used [0x00000000ffe00000,0x00000000ffefc888,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 10240K, used 6680K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 65% used [0x00000000fec00000,0x00000000ff286298,0x00000000ff600000) Metaspace used 3305K, capacity 4500K, committed 4864K, reserved 1056768K class space used 357K, capacity 388K, committed 512K, reserved 1048576KPSYoungGen 使用了Parallel收集器,关于JVM垃圾收集器漫谈,能够参考《JVM 之(5)垃圾收集器》。
分配allocation4对象的语句会发生一次MinorGC,给allocation4分配内存时,发现Eden已经被占用了6MB,剩余空间已不足分配all4所需的4MB内存,所以发生一次MinorGC。高并发
GC期间,虚拟机又发现已有的3个2MB大小的对象所有没法放入Survivor空间(Survivor空间只有1MB大小),因此只好经过分配担保机制提早分配到老年代去。性能
GC结束后,4MBallocation4对象顺利分配在Eden中,Survivor空闲,老年代被占用6MB。
this
对于体积较大的对象,直接进入老年代区域而不是分配到新生代。
JVM参数-XX:PretenureSizeThreshold的意思就是将体积大于这个设置值的对象直接在老年代分配。 这样作是为了不在Eden区及两个Survivor区之间发生大量的内存复制。
PretenureSizeThreshold参数只对 Serial 和 ParNew两款收集器有效。
spa
private static final int _1MB = 1024 * 1024; public static void main(String[] args) { byte[] allocation = new byte[7 * _1MB]; }
PSYoungGen total 9216K, used 4537K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 55% used [0x00000000ff600000,0x00000000ffa6e478,0x00000000ffe00000) from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) ParOldGen total 10240K, used 7168K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 70% used [0x00000000fec00000,0x00000000ff300010,0x00000000ff600000) Metaspace used 3326K, capacity 4500K, committed 4864K, reserved 1056768K class space used 361K, capacity 388K, committed 512K, reserved 1048576K
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。
为了作到这点,虚拟机给每一个对象定义了一个对象年龄(Age)计数器。 若是对象在Eden出生并通过第一次Minor GC后仍然存活,而且能被Survivor容纳的话,将被移动到Survivor空间中,而且对象年龄设为1。
对象在Survivor区中每“熬过”一次Minor GC,年龄就增长1岁,当它的年龄增长到必定程度(默认为15岁),就将会被晋升到老年代中。
对象晋升老年代的年龄阈值,能够经过参数-XX:MaxTenuringThreshold设置。
.net
为了能更好地适应不一样程序的内存情况,虚拟机并非永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代。
若是在Survivor空间中相同年龄全部对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就能够直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
线程
若是小于,或者-XX:HandlePromotionFailure设置不容许冒险,那这时也要改成进行一次Full GC。日志
触发Full GC执行的状况:
1. System.gc();
2 . 老年代空间不足
3. Permanet Generation空间满
4. CMS GC时出现promotion failed和concurrent mode failure(《JVM 之(5)垃圾收集器》中提到)
5. 统计获得的Minor GC晋升到老年代的平均大小大于旧生代的剩余空间
对象
public class StackAllocation { public StackAllocation obj; /** * 方法返回StackAllocation对象,发生逃逸 * @return */ public StackAllocation getInstance(){ return obj == null ? new StackAllocation() : obj; } /** * 为成员变量赋值,发生逃逸 */ public void setObj(){ this.obj = new StackAllocation(); } /** * 对象做用域在方法体内,没有发生逃逸 */ public void useStackAllocation(){ StackAllocation stackAllocation = new StackAllocation(); } /** * 引用成员变量,发生逃逸 */ public void useStackAllocationObj(){ StackAllocation stackAllocation = getInstance(); } }
5. 逃逸分析/栈上分配的优劣分析
优点表如今如下两个方面: