一、为什么会有年轻代

  我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能。你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到哪些对象没用,这样就会对堆的所有区域进行扫描。而我们的很多对象都是朝生夕死的,如果分代的话,我们把新创建的对象放到某一地方,当GC的时候先把这块存“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。

二.年轻代中的GC

  新生代大小(PSYoungGen total 9216K)=eden大小(eden space 8192K)+1个survivor大小(from space 1024K)

  HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8(Eden):1(一个survivor),为啥默认会是这个比例,接下来我们会聊到。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。
  因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

三、一个对象的这一辈子

  我是一个普通的Java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。在年老代里,我生活了20年(每次GC加一岁),然后被回收。

四、为什么要有Survivor区

先不去想为什么有两个Survivor区,第一个问题是,设置Survivor区的意义在哪里? 

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。你也许会问,执行时间长有什么坏处?频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。

好,那我们来想想在没有Survivor的情况下,有没有什么解决办法,可以避免上述情况:

方案 优点 缺点
增加老年代空间 更多存活对象才能填满老年代。降低Full GC频率 随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长
减少老年代空间 Full GC所需时间减少 老年代很快被存活对象填满,Full GC频率增加

显而易见,没有Survivor的话,上述两种解决方案都不能从根本上解决问题。

我们可以得到第一条结论:Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

五、为什么要设置两个Survivor区

设置两个Survivor区最大的好处就是解决了碎片化,下面我们来分析一下。

为什么一个Survivor区不行?第一部分中,我们知道了必须设置Survivor区。假设现在只有一个survivor区,我们来模拟一下流程: 
刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。 
我绘制了一幅图来表明这个过程。其中色块代表对象,白色框分别代表Eden区(大)和Survivor区(小)。Eden区理所当然大一些,否则新建对象很快就导致Eden区满,进而触发Minor GC,有悖于初衷。 

碎片化带来的风险是极大的,严重影响Java程序的性能。堆空间被散布的对象占据不连续的内存,最直接的结果就是,堆中没有足够大的连续内存空间,接下去如果程序需要给一个内存需求很大的对象分配内存。。。画面太美不敢看。。。这就好比我们爬山的时候,背包里所有东西紧挨着放,最后就可能省出一块完整的空间放相机。如果每件行李之间隔一点空隙乱放,很可能最后就要一路把相机挂在脖子上了。

那么,顺理成章的,应该建立两块Survivor区,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代中。下图中每部分的意义和上一张图一样,就不加注释了。 

上述机制最大的好处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片。

那么,Survivor为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果Survivor区再细分下去,每一块的空间就会比较小,很容易导致Survivor区满,因此,我认为两块Survivor区是经过权衡之后的最佳方案。

参考:
http://blog.csdn.net/antony9118/article/details/51425581

http://stackoverflow.com/questions/21476348/java-gc-why-two-survivor-spaces

六、有关年轻代的JVM参数

1)-XX:NewSize和-XX:MaxNewSize(jdk1.3or1.4)

用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。

2)-Xmn(jdk1.4or lator)

用于设置年轻代大小。例如:-Xmn10m,设置新生代大小为10m。此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是(eden+1 survivor space)不同的。

3)-XX:SurvivorRatio

用于设置Eden和其中一个Survivor的比值,默认比例为8(Eden):1(一个survivor),这个值也比较重要。

例如:-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。

例子:-XX:SurvivorRatio=8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

package com.jvm.study.part3;

import java.util.concurrent.TimeUnit;

public class GCTest {

    private static final int _1MB = 1024 * 1024;

    /**
* @VM args:-verbose:gc -Xms20m -Xmx20m -Xmn10m -XX:SurvivorRatio=8
*/
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
System.out.println("1");
allocation2 = new byte[2 * _1MB];
System.out.println("2");
allocation3 = new byte[2 * _1MB];
System.out.println("3");
allocation4 = new byte[2 * _1MB];
System.out.println("4"); }
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(30);
testAllocation();
} }

结果:

1
2
3
[GC (Allocation Failure) [PSYoungGen: 7136K->632K(9216K)] 7136K->6784K(19456K), 0.0084250 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 632K->0K(9216K)] [ParOldGen: 6152K->6635K(10240K)] 6784K->6635K(19456K), [Metaspace: 2562K->2562K(1056768K)], 0.0092126 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
4
Heap
PSYoungGen total 9216K, used 2290K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 27% used [0x00000000ff600000,0x00000000ff83c960,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 6635K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 64% used [0x00000000fec00000,0x00000000ff27afa8,0x00000000ff600000)
Metaspace used 2569K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 277K, capacity 386K, committed 512K, reserved 1048576K

结果分析:

1、-Xmn=10m,但实际的新生代大小(PSYoungGen total 9216K)=eden大小(eden space 8192K)+1个survivor大小(from space 1024K)

4)-XX:+PrintTenuringDistribution

这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。

5).-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold

用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1。

转自http://www.cnblogs.com/duanxz/p/6076662.html

JVM内存结构--新生代及新生代里的两个Survivor区(下一轮S0与S1交换角色,如此循环往复)、常见调优参数的更多相关文章

  1. JVM内存结构之二--新生代及新生代里的两个Survivor区(下一轮S0与S1交换角色,如此循环往复)、常见调优参数

    一.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我 ...

  2. JVM体系结构之六:堆Heap之2:新生代及新生代里的两个Survivor区(下一轮S0与S1交换角色,如此循环往复)、常见调优参数

    一.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我 ...

  3. JDK8的JVM内存结构,元空间替代永久代成为方法区及常量池的变化

    JVM的知识这里总结的很详细:https://github.com/doocs/jvm/blob/master/README.md,因此在本博客也不会再对其中的东西重复总结了. 现在很多文章关于JVM ...

  4. 在JVM的新生代内存中,为什么除了Eden区,还要设置两个Survivor区

    在JVM的新生代内存中,为什么除了Eden区,还要设置两个Survivor区? 1 为什么要有Survivor区 先不去想为什么有两个Survivor区,第一个问题是,设置Survivor区的意义在哪 ...

  5. 为什么新生代内存需要有两个Survivor区

    转载自:http://blog.csdn.net/antony9118/article/details/51425581 在JVM的新生代内存中,为什么除了Eden区,还要设置两个Survivor区? ...

  6. 为什么新生代内存需要有两个Survivor区?

    对于常见的GC算法,我们都应该知道,例如:标记清除算法.复制算法.标记整理算法等.标记清除算法由于回收之后存在大量的内存碎片,存在效率和空间问题!为了解决效率问题,引出了复制算法!熟悉GC算法的小伙伴 ...

  7. 新生代内存中为什么要有两个survivor区

    首先是关于新生代中的内存分布的描述: 新生代中的对象都是“朝生夕死”的对象,所以每次gc存活的对象很少,于是在新生代中采用的垃圾回收算法是“复制算法”. 将新生代的内存分为一块较大的Eden区域和两块 ...

  8. java线程基础巩固---多线程与JVM内存结构的关系及Thread构造函数StackSize的理解

    继续学习一下Thread的构造函数,在上次[http://www.cnblogs.com/webor2006/p/7760422.html]已经对如下构造都已经学习过了: 多线程与JVM内存结构的关系 ...

  9. jvm系列(二):JVM内存结构

    JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...

随机推荐

  1. 【UOJ448】【集训队作业2018】人类的本质 min_25筛

    题目大意 给你 \(n,m\),求 \[ \sum_{i=1}^n\sum_{x_1,x_2,\ldots,x_m=1}^i\operatorname{lcm}(\gcd(i,x_1),\gcd(i, ...

  2. openflow packet_out和packet_in分析

    任务目的 1. 掌握OpenFlow交换机发送Packet-in消息过程及其消息格式. 2. 掌握OpenFlow控制器发送Packet-out消息过程及其消息格式. 实验原理 Packet-In 使 ...

  3. redisson整合spring

    转: redisson整合spring 转: 原文:http://blog.csdn.net/wang_keng/article/details/73549274 首先讲下什么是Redisson:Re ...

  4. <02>labSQL的配置和使用方法

    任务布置:制作简单地铁站点管理系统<2> 要求一:正确配置系统,建立基本正常的数据通道:要求二:实现地铁站点的登记,拥有查询功能: 正文: 今天介绍labview虚拟仪器软件中  labS ...

  5. python类方法以及类调用实例方法的理解

    classmethod类方法 1) 在python中.类方法 @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法. 类方法的第一个参数cls,而 ...

  6. Java基础--面向对象编程3(继承)

    1.继承的作用 为了提取两个类中公共的代码,可以使用继承抽取重复性的代码到一个公共类中. 这个公共的类称为父类(super class),继承于父类的类称为子类(sub class). 2.java继 ...

  7. Primavera——免费版本的下载和使用

    1.oracle收购了Primavera后,开启了以下版本的免费使用. 进入License Codes查看目前支持的版本,并下载与下载版本的无线许可文件. 2.下载软件 进入Oracle Softwa ...

  8. 彻底解决MacOS上应用程序快捷键冲突的问题,自定义快捷键设置

    1看图操作 上面选择好你要修改的应用程序的快捷键 ,我以Chrome为例子 最后点击下ADD 然后回到Chrome的菜单,发现刷新页的快捷键变成了F5 注意,快捷键的名字要和你Chrome菜单上的名字 ...

  9. 《Link Prediction with Personalized Social Influence》论文解读

    论文:Huo, Zepeng, Xiao Huang, and Xia Hu. "Link Prediction with Personalized Social Influence.&qu ...

  10. 2018-2019-2 20175235 实验二《Java面向对象程序设计》实验报告

    实验内容: 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L.I.D原则 5. 了解设计模式 一.单元测试 1伪代码 百 ...