内存申请过程 1、JVM会试图为相关Java对象在Eden中初始化一块内存区域; 2、当Eden空间足够时,内存申请结束。否则到下一步; 3、JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区; 4、Survivor区被用来作为Eden及old的中间交换区域,如果Survivor不足以放置eden区的对象,如果old区有空闲,那么直接放置在old区,Survivor区的对象会被移到Old区 5、当old区空间不够时,JVM会在old区进行major collection; 完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误"; [b] 1、jvm优先分配在eden区 2、当Eden空间足够时,内存申请结束。 [/b] 证明: jvm参数设置:

-Xmx20M -Xms20M -Xmn10M -verbose:gc  -XX:SurvivorRatio=8 -XX:+PrintGCDetails

其中:

-Xmx20M : 堆最大内存是20M

-Xms20M: 初始化也是20M

-Xmn10M :新生代10M,老年代10M

-verbose:gc 输出退出后的日志

-XX:SurvivorRatio=8:eden 为8M s0 s1 各1M

-XX:+PrintGCDetails:打印gc日志的详情

代码:

public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*2];
	}
}

日志输出:

Heap
 def new generation   total 9216K, used 2704K [0x03ac0000, 0x044c0000, 0x044c0000)
  eden space 8192K,  33% used [0x03ac0000, 0x03d64230, 0x042c0000)
  from space 1024K,   0% used [0x042c0000, 0x042c0000, 0x043c0000)
  to   space 1024K,   0% used [0x043c0000, 0x043c0000, 0x044c0000)
 tenured generation   total 10240K, used 0K [0x044c0000, 0x04ec0000, 0x04ec0000)
   the space 10240K,   0% used [0x044c0000, 0x044c0000, 0x044c0200, 0x04ec0000)
 compacting perm gen  total 12288K, used 2143K [0x04ec0000, 0x05ac0000, 0x08ec0000)
   the space 12288K,  17% used [0x04ec0000, 0x050d7fe0, 0x050d8000, 0x05ac0000)
No shared spaces configured.

通过分析main方法中申请2m的内存,内存分配到了eden区。from to tenered区都是没有被使用。

[b]

3、JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;

[/b]

证明:

jvm参数设置同上

代码:

public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*9/10];
		byte[] b2 = new byte[1024*1024*8*9/10];
	}
}

日志:

[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 8560K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,  92% used [0x26ea0000, 0x275fc308, 0x276a0000)
  from space 1024K, 100% used [0x277a0000, 0x278a0000, 0x278a0000)
  to   space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
 tenured generation   total 10240K, used 38K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,   0% used [0x278a0000, 0x278a9920, 0x278a9a00, 0x282a0000)
 compacting perm gen  total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fba28, 0x282fbc00, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)

分析:

程序执行逻辑 先申请了1M的内存,然后再次申请8M的内存,考虑内存本身结构会占用内存空间为了避免边界都以9/10的比例来申请。

结果:

先分配了解决1M的内存在eden区,因为再次申请接近8M的内存时,eden区不够,发生了一次young gc,1M的内存分配到了from区,接近8M的内存放在了eden区。

[GC [DefNew: 1250K->1024K(9216K), 0.0015682 secs] 1250K->1062K(19456K), 0.0016006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

其中

9216K 为eden+from = 8M+1M=9M

19456K 为Xmx-to = 20M-1M = 19M

1250K->1024K :本次young gc 年轻代内存的变化

1250K->1062K :本次young gc 总堆内存的变化

[b]

4、Survivor区被用来作为Eden及old的中间交换区域,如果Survivor不足以放置eden区的对象,如果old区有空闲,那么直接放置在old区,Survivor区的对象会被移到Old区

[/b]

证明:

jvm参数设置同上

程序如下:

public class TestEden {
	public static void main(String[] args) {
		byte[] b1 = new byte[1024*1024*5];
		byte[] b2 = new byte[1024*1024*5];
		System.out.println(b1+""+b2);
	}
}

日志输出:

[GC [DefNew: 5448K->140K(9216K), 0.0051311 secs] 5448K->5260K(19456K), 0.0051644 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[B@c17164[B@1fb8ee3
Heap
 def new generation   total 9216K, used 5511K [0x26ea0000, 0x278a0000, 0x278a0000)
  eden space 8192K,  65% used [0x26ea0000, 0x273deb58, 0x276a0000)
  from space 1024K,  13% used [0x277a0000, 0x277c32a8, 0x278a0000)
  to   space 1024K,   0% used [0x276a0000, 0x276a0000, 0x277a0000)
 tenured generation   total 10240K, used 5120K [0x278a0000, 0x282a0000, 0x282a0000)
   the space 10240K,  50% used [0x278a0000, 0x27da0010, 0x27da0200, 0x282a0000)
 compacting perm gen  total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)
   the space 12288K,   2% used [0x282a0000, 0x282fbb28, 0x282fbc00, 0x28ea0000)
    ro space 8192K,  67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)
    rw space 12288K,  53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)

分析:首先程序申请了5M的内存,放在了eden区,然后再次申请5M的内存,eden区不够,发生young gc,5M放置在from区,依然不够,故直接放置old 区

结果:eden区 8192*65/100/1024=5M

old区:10240*50/100/1024=5M

5、如果eden区不够,from区也承载不了,恰old区也已承载不了,那么会full gc

证明:

jvm参数配置同上

java代码:

[code]

public class TestEden {

public static void main(String[] args) {

byte[] b1 = new byte[1024*1024*4];

byte[] b2 = new byte[1024*1024*5];

b1=null;

b2=null;

byte[] b3 = new byte[1024*1024*8];

}

}

[/code]

日志输出:

[code]

[GC [DefNew: 4424K->140K(9216K), 0.0056340 secs] 4424K->4236K(19456K), 0.0056837 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

[GC [DefNew: 5260K->140K(9216K), 0.0008662 secs][Tenured: 4096K->140K(10240K), 0.0084034 secs] 9356K->140K(19456K), [Perm : 366K->366K(12288K)], 0.0093567 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

Heap

def new generation total 9216K, used 163K [0x26ea0000, 0x278a0000, 0x278a0000)

eden space 8192K, 2% used [0x26ea0000, 0x26ec8fc8, 0x276a0000)

from space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000)

to space 1024K, 0% used [0x277a0000, 0x277a0000, 0x278a0000)

tenured generation total 10240K, used 8332K [0x278a0000, 0x282a0000, 0x282a0000)

the space 10240K, 81% used [0x278a0000, 0x280c3228, 0x280c3400, 0x282a0000)

compacting perm gen total 12288K, used 366K [0x282a0000, 0x28ea0000, 0x2c2a0000)

the space 12288K, 2% used [0x282a0000, 0x282fba68, 0x282fbc00, 0x28ea0000)

ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000)

rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000)

[/code]

分析:

1、先申请了4M内存,放在eden区。

2、然后申请5M内存时,发生了young gc,无奈4M被安置在了old区,5M放在了eden区

3、再次向eden区申请8M的时候,eden区不够,同时8M还是放不下eden区,于是要full gc,full gc前先进行了young gc,5M 因为没有引用被清理,8M放在old区时,old区满,故full gc 4M也被清除old区。

结果:10240K, 81% used 为8M放在了old区

[b]

如果old区不够,会发生错误 java.lang.OutOfMemoryError: Java heap space

[/b] 
[code] 
public class TestEden { 
 public static void main(String[] args) { 
 byte[] b1 = new byte[1024*1024*4]; 
 byte[] b2 = new byte[1024*1024*5]; 
 b2=null; 
 byte[] b3 = new byte[1024*1024*8]; 
 } 
} 
[/code]

日志:

[code] 
[GC [DefNew: 4424K->140K(9216K), 0.0057531 secs] 4424K->4236K(19456K), 0.0058056 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[Full GC [Tenured: 4236K->4233K(10240K), 0.0075081 secs] 4236K->4233K(19456K), [Perm : 366K->365K(12288K)], 0.0075750 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
 at test.test.TestEden.main(TestEden.java:8) 
Heap 
 def new generation total 9216K, used 251K [0x26ea0000, 0x278a0000, 0x278a0000) 
 eden space 8192K, 3% used [0x26ea0000, 0x26edefd0, 0x276a0000) 
 from space 1024K, 0% used [0x276a0000, 0x276a0000, 0x277a0000) 
 to space 1024K, 0% used [0x277a0000, 0x277a0000, 0x278a0000) 
 tenured generation total 10240K, used 4233K [0x278a0000, 0x282a0000, 0x282a0000) 
 the space 10240K, 41% used [0x278a0000, 0x27cc2630, 0x27cc2800, 0x282a0000) 
 compacting perm gen total 12288K, used 365K [0x282a0000, 0x28ea0000, 0x2c2a0000) 
 the space 12288K, 2% used [0x282a0000, 0x282fb688, 0x282fb800, 0x28ea0000) 
 ro space 8192K, 67% used [0x2c2a0000, 0x2c802f30, 0x2c803000, 0x2caa0000) 
 rw space 12288K, 53% used [0x2caa0000, 0x2d110180, 0x2d110200, 0x2d6a0000) 
[/code]

分析:

4M 先是放在eden区,5M申请时gc 4M转入old区,5M留在了eden区,8M申请时,发生gc5M在eden区被清理,因为eden区不够8M,所以分配到old区,old区发生full gc,full gc依然腾不出空间,报错。

注意:5M是在eden区gc时被清理,理由:[GC [DefNew: 5260K->140K(9216K), 0.0008379 secs][Tenured: 4096K->4236K(10240K), 0.0092434 secs] 9356K->4236K(19456K), [Perm : 366K->366K(12288K)], 0.0101605 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 日志显示(Tenured: 4096K->4236K(10240K))无变化,但是eden区和堆总内存变化(DefNew: 5260K->140K(9216K), 9356K->4236K(19456K));