1对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC
参数:
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
表示Java堆20MB,10MB新生代,10MB老年代,新生代中一个Eden与一个Survivor大小比8:1
代码:
public class JvmTest {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] allocation1,allocation2,allocation3,allocation4;
allocation1 = new byte[2* _1MB];
allocation2 = new byte[2* _1MB];
allocation3 = new byte[2* _1MB];
allocation4 = new byte[4* _1MB]; //出现一次MinorGC
}
}
在分配allocation4时,Eden已经被占用6MB,剩余不够4MB,因此发生minor gc,并且三个2MB大小的对象全部无法放入1MB大小的Survior区,所以只能通过分配担保机制提前转移到老年代去。
结果
Heap
PSYoungGen total 9216K, used 7291K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 89% used [0x00000000ff600000,0x00000000ffd1efe8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
Metaspace used 2624K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
这里与书中不同,我的垃圾收集器只将两个2MB对象放入OldGen
2 大对象直接进入老年代
在Java虚拟机中要避免大对象的原因是,在分配空间时,他容易导致内存明明还有不少空间时就提前触发垃圾收集。而当复制对象时,大对象需要高额的复制开销。
参数
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
表示大于3MB的对象会被直接放入老年代(不过只对Serial和ParNew有效)
代码
public class JvmTest {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] allocation4 = new byte[4* _1MB]; //出现一次MinorGC
}
}
3长期存活的对象将进入老年代
参数
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution
表示年龄到达1变为老年代
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution
表示年龄到达15变为老年代
代码
public class JvmTest {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] allocation1,allocation2,allocation3; //出现一次MinorGC
allocation1 = new byte[_1MB / 4];
allocation2 = new byte[_1MB * 4];
allocation3 = new byte[_1MB * 4];
allocation3 = null;
allocation3 = new byte[_1MB * 4];
}
}
不过我的结果两种一致,估计是因为参数不适用于我当前的收集器
4动态对象年龄判定
参数与上面的值为15的一致,allocation1和2加起来是512kb,大于survivor的一半,被视为老年代。
public class JvmTest {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] allocation1,allocation2,allocation3,allocation4; //出现一次MinorGC
allocation1 = new byte[_1MB / 4];
allocation2 = new byte[_1MB / 4];
allocation3 = new byte[_1MB * 4];
allocation4 = new byte[_1MB * 4];
allocation4 = null;
allocation4 = new byte[_1MB * 4];
}
}
5 空间分配担保
在老年代最大可用连续空间小于历次晋升到老年代对象的平均大小,也就是有风险的Minor GC
这个时候如果失败会进行Full GC,因此,此时设置HandlePromotionFailure其实就是为了避免Full GC过于频繁。
在JDK 6之后,规则变为,在老年代最大可用连续空间小于历次晋升到老年代对象的平均大小,就进行Full GC,不用设置。
参数
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-HandlePromotionFailure
代码
public class JvmTest {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] allocation1,allocation2,allocation3,allocation4;
byte[] allocation5,allocation6,allocation7;
allocation1 = new byte[_1MB * 2];
allocation2 = new byte[_1MB * 2];
allocation3 = new byte[_1MB * 2];
allocation1 = null;
allocation4 = new byte[_1MB * 2];
allocation5 = new byte[_1MB * 2];
allocation6 = new byte[_1MB * 2];
allocation4 = null;
allocation5 = null;
allocation6 = null;
allocation7 = new byte[_1MB * 2];
}
}
在我的电脑上运行失败,无法识别HandlePromotionFailure