关闭

JVM中将对象预留在新生代

标签: jvm
244人阅读 评论(0) 收藏 举报
分类:

            JVM中将对象预留在新生代

         可以看出JVM的大部分对象在创建时,都是在新生代的Eden中分配内存空间来存储的,只有极少数的对象会被直接分配到老年代。这一现象的是由于JVM对于对象存储设计的分配策略造成的。为了更好的了解这个策略及其对于对象回收机制的影响,笔者认为有必要讲解一下JVM的内存分配和回收策略。首先,来看看JVM将对象分配在新生代情况。

       

         在大多数的情况下,对象产生于新生代的Eden中(除了比较大的对象以外)。当Eden中的空间逐渐被新的对象分配占满时,就会出现Eden区域无法为后面产生的对象分配足够的存储空间的现象。此时,就会引发JVM 进行一次Minor GC。

         由于通过研究发现,大部分的对象都是占用空间比较小而且生命周期很短的,所以对于这些对象来说,其一生基本上都是在Eden或者新生代中度过的。为什么这样说呢?因为对于一些小的对象、生命周期比较短的对象,它们大部分都很难经历几次Minor GC而仍然处于存活状态。大部分对象都在这几次Minor GC的过程中结束了生命周期。进而其占用的内存被系统GC回收了,只有少数一部分才会进入老年代。所以出于对对象生命周期及大小的考虑,把小对象或者生命周期较短的对象放入到新生代,或者预留在新生代是比较合理的,毕竟如果对象过早的放入老年代,会增加Full GC的风险,这是编程开发者极其不远看到的,它会造成巨大的性能问题。

        基于此JVM在新生代划分出了两个Survivor区域用来处理这些预留在新生代的对象,这两个Survivor区域就是From(Survivor1)和To(Survivor2)。并且在进行Minor GC时,新生代一般采用的是复制算法。其具体过程如下:

1)      在为新对象分配内存空间时,发现新生代的Eden区域已满,触发Minor GC;

2)      将Eden区域中的存活的对象复制到To区域;(To未满)

3)      将From区域中仍然存活的对象复制到To区域;(To未满且From中不存在长期存活的对象)

4)      将From区域与To区域互换;

5)      清空Eden与交换后的To区域(原From区域);

从整个过程来看,新的对象在进行前几次Minor GC时,都会被预留在新生代中。但是也有一些潜在的问题,如在进行GC时,To区域满了怎么办?进行对象进行多次Minor GC仍然在新生代,造成新生代内存资源的占用,影响性能等问题。不用着急,这些问题在后面几个小结,都会有解答。

        下面,通过图4-3来为读者形象的解释Minor GC中,对象在新生代中移动。

图4-3 Minor GC 前后新生代内存分布


为了进一步了解,JVM把一些小对象预留在新生代的情况,这里需要读者动手来演示一个简单的例子,更深入的了解这一内存分配策略。

案例代码如下

//案例4.2
package Example;
/**
  *@author DT大数据梦工厂  
  * 新浪微博:http://weibo.com/ilovepains/  
  * Created by pc-Hipparic on 2016/5/9.  
 */
public class Test4_2 {

	private static int KB = 1024;
	public static void main(String[] args) {
		byte[] object1, object2, object3, object4;
		object1 = new byte[204 * KB];
		object2 = new byte[512 * KB];
		object3 = new byte[204 * KB];
		object2 = new byte[100 * KB];
		object4 = new byte[512 * KB];
		object1 = new byte[102 * KB];
		object4 = new byte[500 * KB]; 	//会发生Minor GC
		object3 = new byte[202 * KB];
	}
}

JVM 参数配置如下:

-Xms15M -Xmx15M -Xss128k -XX:+PrintGCDetails -XX:NewRatio=2 -XX:SurvivorRatio=2
-XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:./gclogs

这里,对JVM做一些解释,-Xms和-Xmx确定了JVM中的堆的大小为15M,-XX:NewRatio定义了新生代与老年代的的比例为1:2,则可以推测出新生代大小约为4M。-XX:SurvivorRatio定义了Eden与Survivor区域的比例为1:1,则可以推出Eden大小为2M,From与To约为1M。

首先,为了更好的了解程序的GC日志。

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 3584K, used 1880K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 91% used [0x00000000ffb00000,0x00000000ffcd6060,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 11264K, used 0K [0x00000000ff000000, 0x00000000ffb00000, 0x00000000ffb00000)
  object space 11264K, 0% used [0x00000000ff000000,0x00000000ff000000,0x00000000ffb00000)
 Metaspace       used 2704K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 301K, capacity 386K, committed 512K, reserved 1048576K
2016-05-17T23:21:12.248+0800: 0.268: [GC (Allocation Failure) [PSYoungGen: 1880K->1212K(3584K)] 1880K->1220K(14848K), 0.0025642 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 3584K, used 1212K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd00000)
  from space 1536K, 78% used [0x00000000ffd00000,0x00000000ffe2f060,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
 ParOldGen       total 11264K, used 8K [0x00000000ff000000, 0x00000000ffb00000, 0x00000000ffb00000)
  object space 11264K, 0% used [0x00000000ff000000,0x00000000ff002000,0x00000000ffb00000)
 Metaspace       used 2704K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 301K, capacity 386K, committed 512K, reserved 1048576K
}

       由GC前,堆的日志可以看出,在发生第一次Minor GC时,PSYoungGen区域大小为3584K(约为3.5M),其中1880K(约为1.8M),这主要是Eden中的对象的大小。从详细内容来看,只有Eden区域利用率达到了91%即1880K。此时From和To、以及老年代利用率都是0%。此时,是由于Eden区域内存已满,无法在为我们的对象分配内存而引发的。

       通过GC日志可以看出YGC前新生代占用大小为1880K(约1.8M),YGC后新生代占用内存大小为1212K,约为1.2M,而PSYoungGen总大小为3584K(约为3.5M)。YGC前JVM堆内存占用为1881K(约1.8M),YGC后堆内存占用为1220K(约1.2M),JVM对总大小为14848K(约为15M)。

       由GC后,堆的日志日志信息可以看出,此时Eden区和To区域已经被清空了。而From区域则存放所有在Minor GC后仍存活的对象(大小约为1212K)。这里虽然是From区域,就向前面讲解的一样,这是在GC后进过交换From与To区域,使得预留在新生代的对象被存放到了From区域。

       这一小节,我们通过案例深入的了解了对象在Minor GC时,对象会先预留在新生代,

并对Minor GC过程做了讲解。为读者深入了解JVM GC过程奠定了基础。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:112788次
    • 积分:1587
    • 等级:
    • 排名:千里之外
    • 原创:40篇
    • 转载:37篇
    • 译文:0篇
    • 评论:10条
    最新评论