详细分析一下MaxTenuringThreshold在虚拟机垃圾回收中作用及内存分配过程
2017-01-07 16:29
330 查看
/**
*VM Args:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintTenuringDistribution -XX:SurvivorRatio=8
*-XX:+UseParNewGC -XX:MaxTenuringTheshold=1
*/ //以上注释配置了20M堆内存,新生代10M,其中eden8M、survivor或者说from space和to space都是1M。
public class Test
{
private static final int _1MB=1024*1024;
public static void testTenuringThreshold(){
byte[] allocation1,allocation2,allocation3;
allocation1=new byte[_1MB/4];//eden上分配1/4M内存
//什么时候进入老年代取决于XX:MaxTenuringThreshold设置
allocation2=new byte[4*_1MB];//eden上分配4M内存
allocation3=new byte[4*_1MB];//打算在eden上分配4M,但是eden只有8M,一共8+1/4无法分配了(这里eden加上from space是有9M的,但是看下面的GC日志确实发生了GC,猜测大概是eden与survivor不连续,无法提供连续的4M空间吧,这点希望大神指正),引发minorGC(回收时发现对象1、2都还有用,想要复制到to
space,但1M的survicor放不下4.25M数据,只可以放下第一个0.25M的,所以结果是0.25从from space转移到了to space,而2的4M数据直接通过分配担保机制进入了老年代),GC之后呢把对象3的4M数据放入了eden。
allocation3=null;//这里对象3取消了引用,但应该还是没有调动GC的
allocation3=new byte[4*_1MB];//这里又即将分配4M内存,之前eden里有可以回收的4M,加一起共8M,而本身eden中应该有一小部分内存占用,大概几百k,所以还是同样的问题,即可能没有足够的连续4M内存用来分配(这里测试如果将对象3后非配内存改为3M,是不会发生GC的),所以引发第二次MinorGC(这次回收中因为之前的4M已没了引用,可以回收,加上tospace中的1/4M的内存已经经过了两次GC,我们设置的年龄最大阀值是1,所以这部分可以晋升为老年代中,故新生代中原内存4.25M全部清零),最后将新的对象3的4M数据放到eden中。
}
public static void main(String[] args){
Test.testTenuringThreshold();
}
}
[GC[DefNew //表示新生代发生GC
Desired survivor size 524288 bytes, new threshold
4000
1 (max 1)//desired survivor size 表示一个survivor大小的一半,也就是1M的一半0.5M
- age 1: 748784 bytes, 748784 total
: 5188K->731K(9216K), 0.0049848 secs] 5188K->4827K(19456K), 0.0054757 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]//5188->731就表示4M去了老年代分配担保,留下1/4M的数据转到了tospace
[GC[DefNew//第二次新生代GC
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age 1: 136 bytes, 136 total
: 4911K->0K(9216K), 0.0018743 secs] 9007K->4825K(19456K), 0.0020440 secs] [Times//4911->0表示4M无指引的内存被回收,而1/4M的tospace内存因为MaxTenuringThreshold=1所以经过两次GC转为了老年代数据
: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4260K [0x00000000f9a00000, 0x00000000fa4//最终新生代只剩下了新对象3的4M内存
00000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd0, 0x0000000//最终新生代只剩下了新对象3的4M内存
0fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200088, 0x0000000
0fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x0000000
0fa400000)
tenured generation total 10240K, used 4825K [0x00000000fa400000, 0x00000000fa//老年代存了分配担保机制来的4M和年龄晋升来的1/4M数据,共4824K
e00000, 0x00000000fae00000)
the space 10240K, 47% used [0x00000000fa400000, 0x00000000fa8b6730, 0x000000
00fa8b6800, 0x00000000fae00000)
compacting perm gen total 21248K, used 2702K [0x00000000fae00000, 0x00000000fc
2c0000, 0x0000000100000000)
the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb0a3be0, 0x000000
00fb0a3c00, 0x00000000fc2c0000)
No shared spaces configured.
接下来,当MaxTenuringThreshold=15时:为了看清survivor起初有多大的空间被占用,我将allocation1注释掉
// allocation1=new byte[_1MB/4];
allocation2=new byte[4*_1MB];
allocation3=new byte[4*_1MB];
allocation3=null;
allocation3=new byte[4*_1MB];
得到[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 486624 bytes, 486624 total
: 4932K->475K(9216K), 0.0042339 secs] 4932K->4571K(19456K), 0.0047175 secs] [Tim
//4932减少到475,也就是初始不分配任何东西进去也被占用了475K
es: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 136 bytes, 136 total
- age 2: 485136 bytes, 485272 total
: 4655K->473K(9216K), 0.0020539 secs] 8751K->4569K(19456K), 0.0023532 secs] [Tim
//这里4655减少到473,可见两次GC之后survivor中数据并没有到老年代去
es: user=0.00 sys=0.00, real=0.00 secs]
这里我再将allocation1分配37K的内存,也就是将上述分配第一行改为allocation1=new byte[37*1024];
得到:[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age 1: 524528 bytes, 524528 total
: 4969K->512K(9216K), 0.0039282 secs] 4969K->4608K(19456K), 0.0042211 secs] [Tim
//这里4969减少到512K
es: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
: 4608K->0K(9216K), 0.0019568 secs] 8704K->4608K(19456K), 0.0024216 secs] [Times
//这里4608减少到0,也就是说survivor中数据去了老年代,但是设置了MaxTenuringThreshold=15,本要经过15次GC,这里要提到刚才设置的allocation1的37K数据,37+475=512,而512K数据正好是survivor的一半,这里是根据动态对象年龄判断规则(如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄)。所以这里第一次GC37K数据从eden区复制到了survivor,加上本身survivor的475K一共512K,在第二次GC的时候这512K数据符合规则直接去了老年代。
: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4262K [0x00000000f9a00000, 0x00000000fa4
00000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e29b00, 0x0000000
0fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x0000000
0fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x0000000
//由对内存数据也可以看出survivor数据去变为了0
0fa400000)
tenured generation total 10240K, used 4608K [0x00000000fa400000, 0x00000000fa
//老年代数据区存放了分配担保机制过来的4M和512K的数据
e00000, 0x00000000fae00000)
the space 10240K, 45% used [0x00000000fa400000, 0x00000000fa880100, 0x000000
00fa880200, 0x00000000fae00000)
compacting perm gen total 21248K, used 2702K [0x00000000fae00000, 0x00000000fc
2c0000, 0x0000000100000000)
the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb0a3be0, 0x000000
00fb0a3c00, 0x00000000fc2c0000)
No shared spaces configured.
*VM Args:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintTenuringDistribution -XX:SurvivorRatio=8
*-XX:+UseParNewGC -XX:MaxTenuringTheshold=1
*/ //以上注释配置了20M堆内存,新生代10M,其中eden8M、survivor或者说from space和to space都是1M。
public class Test
{
private static final int _1MB=1024*1024;
public static void testTenuringThreshold(){
byte[] allocation1,allocation2,allocation3;
allocation1=new byte[_1MB/4];//eden上分配1/4M内存
//什么时候进入老年代取决于XX:MaxTenuringThreshold设置
allocation2=new byte[4*_1MB];//eden上分配4M内存
allocation3=new byte[4*_1MB];//打算在eden上分配4M,但是eden只有8M,一共8+1/4无法分配了(这里eden加上from space是有9M的,但是看下面的GC日志确实发生了GC,猜测大概是eden与survivor不连续,无法提供连续的4M空间吧,这点希望大神指正),引发minorGC(回收时发现对象1、2都还有用,想要复制到to
space,但1M的survicor放不下4.25M数据,只可以放下第一个0.25M的,所以结果是0.25从from space转移到了to space,而2的4M数据直接通过分配担保机制进入了老年代),GC之后呢把对象3的4M数据放入了eden。
allocation3=null;//这里对象3取消了引用,但应该还是没有调动GC的
allocation3=new byte[4*_1MB];//这里又即将分配4M内存,之前eden里有可以回收的4M,加一起共8M,而本身eden中应该有一小部分内存占用,大概几百k,所以还是同样的问题,即可能没有足够的连续4M内存用来分配(这里测试如果将对象3后非配内存改为3M,是不会发生GC的),所以引发第二次MinorGC(这次回收中因为之前的4M已没了引用,可以回收,加上tospace中的1/4M的内存已经经过了两次GC,我们设置的年龄最大阀值是1,所以这部分可以晋升为老年代中,故新生代中原内存4.25M全部清零),最后将新的对象3的4M数据放到eden中。
}
public static void main(String[] args){
Test.testTenuringThreshold();
}
}
[GC[DefNew //表示新生代发生GC
Desired survivor size 524288 bytes, new threshold
4000
1 (max 1)//desired survivor size 表示一个survivor大小的一半,也就是1M的一半0.5M
- age 1: 748784 bytes, 748784 total
: 5188K->731K(9216K), 0.0049848 secs] 5188K->4827K(19456K), 0.0054757 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]//5188->731就表示4M去了老年代分配担保,留下1/4M的数据转到了tospace
[GC[DefNew//第二次新生代GC
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age 1: 136 bytes, 136 total
: 4911K->0K(9216K), 0.0018743 secs] 9007K->4825K(19456K), 0.0020440 secs] [Times//4911->0表示4M无指引的内存被回收,而1/4M的tospace内存因为MaxTenuringThreshold=1所以经过两次GC转为了老年代数据
: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4260K [0x00000000f9a00000, 0x00000000fa4//最终新生代只剩下了新对象3的4M内存
00000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd0, 0x0000000//最终新生代只剩下了新对象3的4M内存
0fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200088, 0x0000000
0fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x0000000
0fa400000)
tenured generation total 10240K, used 4825K [0x00000000fa400000, 0x00000000fa//老年代存了分配担保机制来的4M和年龄晋升来的1/4M数据,共4824K
e00000, 0x00000000fae00000)
the space 10240K, 47% used [0x00000000fa400000, 0x00000000fa8b6730, 0x000000
00fa8b6800, 0x00000000fae00000)
compacting perm gen total 21248K, used 2702K [0x00000000fae00000, 0x00000000fc
2c0000, 0x0000000100000000)
the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb0a3be0, 0x000000
00fb0a3c00, 0x00000000fc2c0000)
No shared spaces configured.
接下来,当MaxTenuringThreshold=15时:为了看清survivor起初有多大的空间被占用,我将allocation1注释掉
// allocation1=new byte[_1MB/4];
allocation2=new byte[4*_1MB];
allocation3=new byte[4*_1MB];
allocation3=null;
allocation3=new byte[4*_1MB];
得到[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 486624 bytes, 486624 total
: 4932K->475K(9216K), 0.0042339 secs] 4932K->4571K(19456K), 0.0047175 secs] [Tim
//4932减少到475,也就是初始不分配任何东西进去也被占用了475K
es: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 136 bytes, 136 total
- age 2: 485136 bytes, 485272 total
: 4655K->473K(9216K), 0.0020539 secs] 8751K->4569K(19456K), 0.0023532 secs] [Tim
//这里4655减少到473,可见两次GC之后survivor中数据并没有到老年代去
es: user=0.00 sys=0.00, real=0.00 secs]
这里我再将allocation1分配37K的内存,也就是将上述分配第一行改为allocation1=new byte[37*1024];
得到:[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age 1: 524528 bytes, 524528 total
: 4969K->512K(9216K), 0.0039282 secs] 4969K->4608K(19456K), 0.0042211 secs] [Tim
//这里4969减少到512K
es: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
: 4608K->0K(9216K), 0.0019568 secs] 8704K->4608K(19456K), 0.0024216 secs] [Times
//这里4608减少到0,也就是说survivor中数据去了老年代,但是设置了MaxTenuringThreshold=15,本要经过15次GC,这里要提到刚才设置的allocation1的37K数据,37+475=512,而512K数据正好是survivor的一半,这里是根据动态对象年龄判断规则(如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄)。所以这里第一次GC37K数据从eden区复制到了survivor,加上本身survivor的475K一共512K,在第二次GC的时候这512K数据符合规则直接去了老年代。
: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4262K [0x00000000f9a00000, 0x00000000fa4
00000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e29b00, 0x0000000
0fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x0000000
0fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x0000000
//由对内存数据也可以看出survivor数据去变为了0
0fa400000)
tenured generation total 10240K, used 4608K [0x00000000fa400000, 0x00000000fa
//老年代数据区存放了分配担保机制过来的4M和512K的数据
e00000, 0x00000000fae00000)
the space 10240K, 45% used [0x00000000fa400000, 0x00000000fa880100, 0x000000
00fa880200, 0x00000000fae00000)
compacting perm gen total 21248K, used 2702K [0x00000000fae00000, 0x00000000fc
2c0000, 0x0000000100000000)
the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb0a3be0, 0x000000
00fb0a3c00, 0x00000000fc2c0000)
No shared spaces configured.
相关文章推荐
- Java内存的详细分析(包括垃圾回收)
- Dalvik虚拟机为新创建对象分配内存的过程分析
- 深入理解java虚拟机(六):java垃圾收集分析实战(内存分配与回收策略)
- 深入理解java虚拟机(六):java垃圾收集分析实战(内存分配与回收策略)
- Dalvik虚拟机为新创建对象分配内存的过程分析
- Java内存区域分配、存储、垃圾回收策略与回收机制(深入JVM虚拟机)
- 函数调用和栈的内存分配过程分析
- Java的垃圾收集算法、垃圾收集器以及内存分配与回收策略
- Java 学习笔记 (13) - 基本内存分析 和 垃圾回收机制
- 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
- Java的初始化机制、垃圾回收机制和内存分配机制
- 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
- JAVA的内存分配策略和自动垃圾回收机制
- 内存分配和垃圾回收
- JVM中内存回收深入分析,各种垃圾收集器
- 对android虚拟机的理解,包括内存管理机制,垃圾回收机制
- Java内存分配及垃圾回收
- 第3章 垃圾收集器与内存分配策略--《深入理解 Java 虚拟机》笔记
- Java的初始化机制、垃圾回收机制和内存分配机制
- JVM内存分配、垃圾回收、启动参数