精华内容
下载资源
问答
  • 老年代
    2022-03-29 01:49:39

     

    新生代Minor GC后剩余存活对象太多,无法放入Survivor区中,此时就必须将这些存活对
    象直接转移到老年代去,如果此时老年代空间也不够怎么办?
    1、执行任何一次Minor GC之前,JVM会先检查一下老年代可用内存空间,是否大于新生代
    所有对象的总大小,因为在极端情况下,可能新生代Minor GC之后,新生代所有对象都需要
    存活,那就会造成新生代所有对象全部要进入老年代;
    2、如果老年代的可用内存大于新生代所有对象总大小,此时就可以放心大胆的对新生代发起
    一次Minor GC,因为 Minor GC之后即使所有对象都存活,Survivor区放不下了,也可以转
    移到老年代去;
    3、如果执行MInor GC之前,检测发现老年代的可用空间已经小于新生代的全部对象总大小,
    那么就会进行下一个判断,判断老年代的可用空间大小,是否大于之前每一次Minor GC后进
    入老年代的对象的平均大小,如果判断发现老年代的内存大小,大于之前每一次Minor GC
    后进入老年代的对象的平均大小,那么就是说可以冒险尝试一下Minor GC,但是此时真的可
    能有风险,那就是Minor GC过后,剩余的存活对象的大小,大于Survivor空间的大小,也
    大于老年代可用空间的大小,老年代都放不下这些存活对象了,此时就会触发一次“Full GC";
    所以老年代空间分配担保机制的目的?也是为了避免频繁进行Full GC;
    4、如果Full GC之后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那
    么此时就会导致“OOM”内存溢出;

    什么情况下对象会进入老年代
     1、躲过15次GC之后进入老年代,I可通过JVM参数"-XX:MaxTenuring Threshold" 来设置年龄,默认为15岁;
    2、动态对象年龄判断;
    3、老年代空间担保机制;
    4、大对象直接进入老年代;

    大对象是指需要大量连续内存空间的Java对象,比如很长的字符串或者是很大的数组或者
    List集合,大对象在分配空间时,容易导致内存明明还有不少空间时就提前触发垃圾回收以
    获得足够的连续空间来存放它们,而当复制对象时,大对象又会引起高额的内存复制开销,为
    了避免新生代里出现那些大对象,然后屡次躲过GC而进行来回复制,此时JVM就直接把该大,
    对象放入老年代,而不会经过新生代;

    更多相关内容
  • Java垃圾回收(二)老年代垃圾收集器(csdn)————程序
  • jvm 老年代oom gc.log

    2019-11-11 11:10:59
    一次jvm 老年代oom 的dump文件样例
  • JVM之堆内存(年经代,老年代

    千次阅读 2021-03-14 12:43:01
    降低Full GC频率 随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长 减少老年代空间 Full GC所需时间减少 老年代很快被存活对象填满,Full GC频率增加 显而易见,没有Survivor的话,上述两种解决方案都不...

    一、为什么会有年轻代

    我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化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”区被填满之后,会将所有对象移动到年老代中。

    77dc7c8dcf1f088b8121ba870c69885b.png

    三、一个对象的这一辈子

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

    四、为什么要有Survivor区

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

    2fa99ad1b18c112a8a1ac9120c87d6a0.png

    如果没有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,有悖于初衷。

    c1642cd78795a98a0232e1c9a7ac1643.png

    碎片化带来的风险是极大的,严重影响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次,该对象就会被送到老年代中。下图中每部分的意义和上一张图一样,就不加注释了。

    f933167e113e8239a84a1347809840b9.png

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

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

    六、有关年轻代的JVM参数

    88947b2a98c384be392ce8af15a86b64.png

    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。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    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();

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    结果:

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    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

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    29ad300e04bf997fe4aca20a9b9a2b08.png

    结果分析:

    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。

    展开全文
  • 十>JVM新生代、老年代、永久代

    千次阅读 2021-09-29 16:16:58
    (3)老年代 存放活的久的较大对象,因此老年代使用标记整理算法(Full GC)。当老年代容量满时,会触发一次Major GC,回收老年代和年轻代中不再被使用的对象资源。 总结: 1>minor GC是发生在新生代中的垃圾收集,from...

    1、JVM内存模型,如下图,分别是jdk1.8以前和jdk1.8以后的内存模型图 

    (1)Eden区(java堆的年轻代)

    新对象分配内存的地方,由于是所有线程共享的,因此,在堆上分配内存需要加锁

    (2)survival from to(java堆的年轻代)

    在发生一次Minor GC后,from区与to区互换。在发生Minor GC时,Eden区和survivalfrom区会把仍存储的对象复制进survival To去并清除内存。survival to区会把存活更久的对象移至老年代。

    补:a、当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳
    ( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代

    b、但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

    (3)老年代

    存放活的久的较大对象,因此老年代使用标记整理算法(Full GC)。当老年代容量满时,会触发一次Major GC,回收老年代和年轻代中不再被使用的对象资源。

    总结:

    1>minor GC是发生在新生代中的垃圾收集,from 至 To采用的是复制算法

    2>新生代中每次使用的空间不超过90%。主要用来存放新生的对象

    3>Minor GC每次收集后Eden区和Survivor区都被清空;

    4>老年代中使用Full GC,采用标记-清除算法

    5>永久代(方法区)的垃圾回收有两部分内容:废弃常量无用的类

    6>、当一个对象被判定为"死亡"的时候,GC就有责任来回收掉这部分对象的内存空间。新生代是GC收集垃圾的频繁区域。

    7>、另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

    2、GC 日志

    public static void main(String[] args) {
        Object obj = new Object();
        System.gc();
        System.out.println();
        obj = new Object();
        obj = new Object();
        System.gc();
        System.out.println();
    }

     设置 JVM 参数为 -XX:+PrintGCDetails,使得控制台能够显示 GC 相关的日志信息,执行上面代码,下面是其中一次执行的结果。
     


     

       Full GC 信息与 Minor GC 的信息是相似的,这里就不一个一个的画出来了。
       从 Full GC 信息可知,新生代可用的内存大小约为 18M,则新生代实际分配得到的内存空间约为 20M(为什么是 20M? 请继续看下面...)。老年代分得的内存大小约为 42M,堆的可用内存的大小约为 60M。可以计算出: 18432K ( 新生代可用空间 ) + 42112K ( 老年代空间 ) = 60544K ( 堆的可用空间 )
        新生代约占堆大小的 1/3,老年代约占堆大小的 2/3。也可以看出,GC 对新生代的回收比较乐观,而对老年代以及方法区的回收并不明显或者说不及新生代。
    并且在这里 Full GC 耗时是 Minor GC 的 22.89 倍。

    3、JVM参数选项

    jvm 可配置的参数选项可以参考 Oracle 官方网站给出的相关信息:Java HotSpot VM Options
    下面只列举其中的几个常用和容易掌握的配置选项

    /**
      -Xms60m
      -Xmx60m
      -Xmn20m
      -XX:NewRatio=2 ( 若 Xms = Xmx, 并且设定了 Xmn, 那么该项配置就不需要配置了 )
      -XX:SurvivorRatio=8
      -XX:PermSize=30m
      -XX:MaxPermSize=30m
      -XX:+PrintGCDetails
     */
     public static void main(String[] args) {
         new Test().doTest();
     }
     
     public void doTest(){
         Integer M = new Integer(1024 * 1024 * 1);  //单位, 兆(M)
         byte[] bytes = new byte[1 * M]; //申请 1M 大小的内存空间
         bytes = null;  //断开引用链
         System.gc();   //通知 GC 收集垃圾
         System.out.println();
         bytes = new byte[1 * M];  //重新申请 1M 大小的内存空间
         bytes = new byte[1 * M];  //再次申请 1M 大小的内存空间
         System.gc();
         System.out.println();
     }

    按上面代码中注释的信息设定 jvm 相关的参数项,并执行程序,下面是一次执行完成控制台打印的结果:

    [ GC [ PSYoungGen:  1351K -> 288K (18432K) ]  1351K -> 288K (59392K), 0.0012389 secs ]  [ Times: user=0.00 sys=0.00, real=0.00 secs ] 
    [ Full GC (System)  [ PSYoungGen:  288K -> 0K (18432K) ]  [ PSOldGen:  0K -> 160K (40960K) ]  288K -> 160K (59392K)  [ PSPermGen:  2942K -> 2942K (30720K) ],  0.0057649 secs ] [ Times:  user=0.00  sys=0.00,  real=0.01 secs ] 

    [ GC [ PSYoungGen:  2703K -> 1056K (18432K) ]  2863K -> 1216K(59392K),  0.0008206 secs ]  [ Times: user=0.00 sys=0.00, real=0.00 secs ] 
    [ Full GC (System)  [ PSYoungGen:  1056K -> 0K (18432K) ]  [ PSOldGen:  160K -> 1184K (40960K) ]  1216K -> 1184K (59392K)  [ PSPermGen:  2951K -> 2951K (30720K) ], 0.0052445 secs ]  [ Times: user=0.02 sys=0.00, real=0.01 secs ] 

    Heap
     PSYoungGen      total 18432K, used 327K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000)
      eden space 16384K, 2% used [0x00000000fec00000,0x00000000fec51f58,0x00000000ffc00000)
      from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
      to   space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
     PSOldGen        total 40960K, used 1184K [0x00000000fc400000, 0x00000000fec00000, 0x00000000fec00000)
      object space 40960K, 2% used [0x00000000fc400000,0x00000000fc5281f8,0x00000000fec00000)
     PSPermGen       total 30720K, used 2959K [0x00000000fa600000, 0x00000000fc400000, 0x00000000fc400000)
      object space 30720K, 9% used [0x00000000fa600000,0x00000000fa8e3ce0,0x00000000fc400000)

    从打印结果可以看出,堆中新生代的内存空间为 18432K ( 约 18M ),eden 的内存空间为 16384K ( 约 16M),from / to survivor 的内存空间为 2048K ( 约 2M)。


    这里所配置的 Xmn 为 20M,也就是指定了新生代的内存空间为 20M,可是从打印的堆信息来看,新生代怎么就只有 18M 呢? 另外的 2M 哪里去了? 


    别急,是这样的。新生代 = eden + from + to = 16 + 2 + 2 = 20M,可见新生代的内存空间确实是按 Xmn 参数分配得到的。
    而且这里指定了 SurvivorRatio = 8,因此,eden = 8/10 的新生代空间 = 8/10 * 20 = 16M。from = to = 1/10 的新生代空间 = 1/10 * 20 = 2M。
    堆信息中新生代的 total 18432K 是这样来的: eden + 1 个 survivor = 16384K + 2048K = 18432K,即约为 18M。
    因为 jvm 每次只是用新生代中的 eden 和 一个 survivor,因此新生代实际的可用内存空间大小为所指定的 90%。


    因此可以知道,这里新生代的内存空间指的是新生代可用的总的内存空间,而不是指整个新生代的空间大小。


    另外,可以看出老年代的内存空间为 40960K ( 约 40M ),堆大小 = 新生代 + 老年代。因此在这里,老年代 = 堆大小 - 新生代 = 60 - 20 = 40M。


    最后,这里还指定了 PermSize = 30m,PermGen 即永久代 ( 方法区 ),它还有一个名字,叫非堆,主要用来存储由 jvm 加载的类文件信息、常量、静态变量等

    展开全文
  • 详解Java GC、新生代、老年代

    千次阅读 2021-02-13 22:38:12
    在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old)。新生代 ( Young ) 又被划分为三个区域:Eden、S0、S1。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及...

    Java 中的堆是 JVM 所管理的最大的一块内存空间,堆主要用于存放各种类的实例对象和数组。如下图所示: 
    这里写图片描述
    在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old)。新生代 ( Young ) 又被划分为三个区域:Eden、S0、S1。 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

    Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )

    1.年轻代 


    年轻代用来存放新近创建的对象,尺寸随堆大小的增大和减小而相应的变化,默认值是保持为堆大小的1/15,可以通过 -Xmn 参数设置年轻代为固定大小,也可以通过 -XX:NewRatio 来设置年轻代与年老代的大小比例,年轻代的特点是对象更新速度快,在短时间内产生大量的“死亡对象”。

    年轻代的特点是产生大量的死亡对象,并且要是产生连续可用的空间, 所以使用复制清除算法和并行收集器进行垃圾回收.对年轻代的垃圾回收称作初级回收 (minor gc)。

    初级回收将年轻代分为三个区域, 一个新生代 , 2个大小相同的复活代, 应用程序只能使用一个新生代和一个复活代, 当发生初级垃圾回收的时候,gc挂起程序, 然后将新生代和复活代中的存活对象复制到另外一个非活动的复活代中,然后一次性清除新生代和复活代,将原来的非复活代标记成为活动复活代。将在指定次数回收后仍然存在的对象移动到老年代中,初级回收后,得到一个空的可用的新生代。

    新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。 当一个对象被判定为 “死亡” 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的频繁区域。 当对象在 Eden 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳,则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域中,然后清理所使用过的 Eden 以及 Survivor 区域,并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。 但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

    2.老年代 

      主要存放应用程序中生命周期长的内存对象。

       老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC 前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。

       当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

       MajorGC采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM (Out of Memory)异常

    3.永久代 

    指内存的永久保存区域,主要存放Class 和Meta (元数据)的信息,Class在被加载的时候被放入永久区域,它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class 的增多而胀满,最终抛出OOM异常。

        JAVA8与元数据 :

        在Java8 中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

        元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

        类的元数据放入native memory,字符串池和类的静态变量放入java 堆中,这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

    GC日志

    设置 JVM 参数为 -XX:+PrintGCDetails,使得控制台能够显示 GC 相关的日志信息,执行上面代码,下面是其中一次执行的结果。

    这里写图片描述

    这里写图片描述

    Full GC 信息与 Minor GC 的信息是相似的,这里就不一个一个的画出来了。从 Full GC 信息可知,新生代可用的内存大小约为 18M,则新生代实际分配得到的内存空间约为 20M(为什么是 20M? 请继续看下面…)。老年代分得的内存大小约为 42M,堆的可用内存的大小约为 60M。可以计算出: 18432K ( 新生代可用空间 ) + 42112K ( 老年代空间 ) = 60544K ( 堆的可用空间 )新生代约占堆大小的 1/3,老年代约占堆大小的 2/3。也可以看出,GC 对新生代的回收比较乐观,而对老年代以及方法区的回收并不明显或者说不及新生代。并且在这里 Full GC 耗时是 Minor GC 的 22.89 倍。

    JVM参数选项

    jvm 可配置的参数选项可以参考 Oracle 官方网站给出的相关信息:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
    下面只列举其中的几个常用和容易掌握的配置选项

    配置参数功能
    -Xms初始堆大小。如:-Xms256m
    -Xmx最大堆大小。如:-Xmx512m
    -Xmn新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
    -XssJDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。
    -XX:NewRatio新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
    -XX:SurvivorRatio新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
    -XX:PermSize永久代(方法区)的初始大小
    -XX:MaxPermSize永久代(方法区)的最大值
    -XX:+PrintGCDetails打印 GC 信息
    -XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用

    注意:PermSize永久代的概念在jdk1.8中已经不存在了,取而代之的是metaspace元空间,当认为执行永久代的初始大小以及最大值是jvm会给出如此下提示:
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=30m; support was removed in 8.0
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=30m; support was removed in 8.0

    实例分析

    /** 
      -Xms60m 
      -Xmx60m 
      -Xmn20m 
      -XX:NewRatio=2 ( 若 Xms = Xmx, 并且设定了 Xmn, 那么该项配置就不需要配置了 ) 
      -XX:SurvivorRatio=8 
      -XX:PermSize=30m 
      -XX:MaxPermSize=30m 
      -XX:+PrintGCDetails
      */
    public class TestVm{
        public void doTest(){
             Integer M = new Integer(1024 * 1024 * 1);  //单位, 兆(M)          
                byte[] bytes = new byte[1 * M]; //申请 1M 大小的内存空间                 
                bytes = null;  //断开引用链
                System.gc();   //通知 GC 收集垃圾                           
                System.out.println();       //                           
                bytes = new byte[1 * M];  //重新申请 1M 大小的内存空间                      
                bytes = new byte[1 * M];  //再次申请 1M 大小的内存空间                     
                System.gc();
                System.out.println();                                                         
            }
        public static void main(String[] args) {
             new TestVm().doTest();
         }
    }

    按上面代码中注释的信息设定 jvm 相关的参数项,并执行程序,下面是一次执行完成控制台打印的结果:

    [ GC [ PSYoungGen:  1351K -> 288K (18432K) ]  1351K -> 288K (59392K), 0.0012389 secs ]  [ Times: user=0.00 sys=0.00, real=0.00 secs ] 
    [ Full GC (System)  [ PSYoungGen:  288K -> 0K (18432K) ]  [ PSOldGen:  0K -> 160K (40960K) ]  288K -> 160K (59392K)  [ PSPermGen:  2942K -> 2942K (30720K) ],  0.0057649 secs ] [ Times:  user=0.00  sys=0.00,  real=0.01 secs ] 
    
    [ GC [ PSYoungGen:  2703K -> 1056K (18432K) ]  2863K -> 1216K(59392K),  0.0008206 secs ]  [ Times: user=0.00 sys=0.00, real=0.00 secs ] 
    [ Full GC (System)  [ PSYoungGen:  1056K -> 0K (18432K) ]  [ PSOldGen:  160K -> 1184K (40960K) ]  1216K -> 1184K (59392K)  [ PSPermGen:  2951K -> 2951K (30720K) ], 0.0052445 secs ]  [ Times: user=0.02 sys=0.00, real=0.01 secs ] 
    
    
    Heap 
    PSYoungGen      
          total 18432K, used 327K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000)  
          eden  space 16384K, 2% used [0x00000000fec00000,0x00000000fec51f58,0x00000000ffc00000)   
          from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)  
          to   space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000) 
    
    PSOldGen        
        total 40960K, used 1184K [0x00000000fc400000, 0x00000000fec00000, 0x00000000fec00000)  
    
     PSPermGen       
         total 30720K, used 2959K [0x00000000fa600000, 0x00000000fc400000, 0x00000000fc400000)  
         object space 30720K, 9% used [0x00000000fa600000,0x00000000fa8e3ce0,0x00000000fc400000)

    从打印结果可以看出,堆中新生代的内存空间为 18432K ( 约 18M ),eden 的内存空间为 16384K ( 约 16M),from / to survivor 的内存空间为 2048K ( 约 2M)。

    这里所配置的 Xmn 为 20M,也就是指定了新生代的内存空间为 20M,可是从打印的堆信息来看,新生代怎么就只有 18M 呢? 另外的 2M 哪里去了? 别急,是这样的。新生代 = eden + from + to = 16 + 2 + 2 = 20M,可见新生代的内存空间确实是按 Xmn 参数分配得到的。而且这里指定了 SurvivorRatio = 8,因此,eden = 8/10 的新生代空间 = 8/10 * 20 = 16M。from = to = 1/10 的新生代空间 = 1/10 * 20 = 2M。堆信息中新生代的 total 18432K 是这样来的: eden + 1 个 survivor = 16384K + 2048K = 18432K,即约为 18M。因为 jvm 每次只是用新生代中的 eden 和 一个 survivor,因此新生代实际的可用内存空间大小为所指定的 90%。因此可以知道,这里新生代的内存空间指的是新生代可用的总的内存空间,而不是指整个新生代的空间大小。另外,可以看出老年代的内存空间为 40960K ( 约 40M ),堆大小 = 新生代 + 老年代。因此在这里,老年代 = 堆大小 - 新生代 = 60 - 20 = 40M。
    最后,这里还指定了 PermSize = 30m,PermGen 即永久代 ( 方法区 ),它还有一个名字,叫非堆,主要用来存储由 jvm 加载的类文件信息、常量、静态变量等。

    回到 doTest() 方法中,可以看到代码在第 14、18、19 这三行中分别申请了一块 1M 大小的内存空间,并在 16 和 20 这两行中分别显式的调用了 System.gc()。从控制台打印的信息来看,每次调 System.gc(),是先进行 Minor GC,然后再进行 Full GC。
    第 16 行触发的 Minor GC 收集分析:
    从信息 PSYoungGen : 1351K -> 288K,可以知道,在第 14 行为 bytes 分配的内存空间已经被回收完成。引起 GC 回收这 1M 内存空间的因素是第 15 行的 bytes = null; bytes 为 null 表明之前申请的那 1M 大小的内存空间现在已经没有任何引用变量在使用它了,并且在内存中它处于一种不可到达状态 ( 即没有任何引用链与 GC Roots 相连 )。那么,当 Minor GC 发生的时候,GC 就会来回收掉这部分的内存空间。
    第 16行触发的 Full GC 收集分析:
    在 Minor GC 的时候,信息显示 PSYoungGen : 1351K -> 288K,再看看 Full GC 中显示的 PSYoungGen : 288K -> 0K,可以看出,Full GC 后,新生代的内存使用变成0K 了,那么这 288K 到底哪去了 ? 难道都被 GC 当成垃圾回收掉了 ? 当然不是了。我还特意在 main 方法中 new 了一个 Test 类的实例,这里的 Test 类的实例属于小对象,它应该被分配到新生代内存当中,现在还在调用这个实例的 doTest 方法呢,GC 不可能在这个时候来回收它的。

    接着往下看 Full GC 的信息,会发现一个很有趣的现象,PSOldGen: 0K -> 160K,可以看到,Full GC 后,老年代的内存使用从 0K 变成了 160K,想必你已经猜到大概是怎么回事了。当 Full GC 进行的时候,默认的方式是尽量清空新生代 ( YoungGen ),因此在调 System.gc() 时,新生代 ( YoungGen ) 中存活的对象会提前进入老年代。

    第 20行触发的 Minor GC 收集分析:
    从信息 PSYoungGen : 2703K -> 1056K,可以知道,在第 18行创建的,大小为 1M 的数组被 GC 回收了。在第 19 行创建的,大小也为 1M 的数组由于 bytes 引用变量还在引用它,因此,它暂时未被 GC 回收。

    第 20 行触发的 Full GC 收集分析:
    在 Minor GC 的时候,信息显示 PSYoungGen : 2703K -> 1056K,Full GC 中显示的 PSYoungGen : 1056K -> 0K,以及 PSOldGen: 160K -> 1184K,可以知道,新生代 ( YoungGen ) 中存活的对象又提前进入老年代了。

    参考资料:

    https://blog.csdn.net/GYQJN/article/details/49848473

    https://www.cnblogs.com/crazy-lc/archive/2020/04/05/12636927.html

     

     

    展开全文
  • 一、对象何时进入老年代(1)当对象首次创建时, 会放在新生代的eden区, 若没有GC的介入,会一直在eden区, GC后,是可能进入survivor区或者年老代(2)当对象年龄达到一定的大小 ,就会离开年轻代, 进入老年代。...
  • Java中的新生代、老年代和永久代

    千次阅读 2020-07-21 14:59:00
    老年代 老年代的对象比较稳定,所以老年代GC不会频繁执行。 在进行老年代GC前一般都先进行了一次新生代GC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大...
  • 晋升的四种方式 担保机制 大对象直接进入老年代 长期存活的对象 动态年龄判断 1、担保机制 1.1什么是担保机制 我们都用过花呗借呗、或者信用卡吧? 就是你身上的前不够用的时候,你可以去借信用卡、借花呗、借借呗,...
  • 深入jvm之对象如何进入老年代

    千次阅读 2021-09-13 15:26:10
    我们知道,整个jvm堆分为新生代和老年代,新生代的对象在进行垃圾回收的时候,可能会进入到老年代,那么我们知道年轻代的对象是如何进入到老年代的吗? 年轻代空间分配 新生代分成Eden,Survivor To,Survivor From...
  • 1、大对象直接进入老年代 大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 XX:PretenureSizeThreshold 可以设置大 对象的大小,如果对象超过设置大小会直接进入老年 代,不会进入年轻代,这个...
  • 了解一下老年代空间担保规则

    千次阅读 2020-10-01 11:13:00
    老年代担保什么? 当新生代被分配了大对象(该对象大小可以通过参数设置),或者经过Minor GC后,存活下来的对象,Survivor区放不下,那么这些对象都会被分配到老年代老年代想担保就能担保? 老年代空间也是有限的...
  • 老年代和永久代

    千次阅读 2021-12-30 23:20:26
    老年代和永久代
  • 大对象直接进入老年代

    千次阅读 2021-03-09 09:20:20
    大对象直接进入老年代Minor GC的规则大对象直接进入老年代-XX:PretenureSizeThreshold指定大于该数值的对象直接进入老年代,避免在新生代的Eden和两个Survivor区域来回复制,产生大量内存复制操作。缺点:只对Serial...
  • JVM中对象进入老年代的3种情况
  • 【JVM】Java堆内存的新生代和老年代

    千次阅读 2021-05-09 09:49:51
    edn区、s0、s1都属于新生代,tentired属于老年代。s指survivor。 对象会在eden区分配内存,在经历一次新生代垃圾回收后,如果对象还存活,则进入s0或者s1,并且对象的年龄会加1。当年龄增加到一定程度(默认15岁,...
  • 文章目录Pre躲过15次GC之后进入老年代动态对象年龄判断大对象直接进入老年代Minor GC后的对象太多,无法放入Survivor区怎么办?老年代空间分配担保规则老年代垃圾回收算法 Pre 上篇文章已经讲清楚了新生代的垃圾...
  • 从内存回收角度看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为:新生代和老年代。再细致一点儿就是Eden空间、From Survivor空间以及To Survivor空间等。注意:堆=新生代+老年代,不包括永久代...
  • 1.躲过15次GC之后进入老年代 系统刚启动时,创建的各种各样的对象,都是分配在年轻代里。 随着慢慢系统跑着跑着,年轻代满了,就会出发Minor GC ,可能1%的少量存活对像转移到空着的Survivor区中 然后系统继续运行...
  • 应用程序运行于稳定状态时,老年代占用的java堆大小2.应用程序运行于稳定状态时,永久代占用的java堆大小其实就是FullGc后这2个数据的大小②、动手测试:1.测试代码:public class A {int[] storage = new int...
  • JAVA堆内存管理是影响性能主要因素之一。 堆内存溢出是JAVA项目非常常见的故障,在解决该问题之前,必须先了解下JAVA堆内存...JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old...
  • JVM 新生代老年代与GC

    千次阅读 2019-08-06 21:57:51
    Java堆可以分为新生代和老年代两个区,其中新生代又可以分为一个Eden区和两个Survivor区,两个Survivor区分别被命名为From和To以示区分,新生代和老年代的比例为1:2,它们共同组成堆的内存区,所以新生代占堆的1/3...
  • 对象如何进入老年代(转载)

    千次阅读 2021-02-28 14:05:36
    问题:一般而言,对象首次创建会被放置在新生代的eden区,...比如很长的字符串,或者很大的数组等,参数-XX:PretenureSizeThreshold=3145728设置,超过这个参数设置的值就直接进入老年代2:长期存活的对象进入老年代...
  • JVM老年代和新生代的比例

    千次阅读 2022-04-20 08:24:14
    在 Java 中,堆被划分成两个不同的区域:新生代( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,...
  • 老年代与标记复制算法

    千次阅读 2021-12-31 07:40:36
    老年代与标记复制算法
  • 把分代理论放到虚拟机里就形成了我们经常听到的老年代和新生代两个区域,每次垃圾回收都会有大量的对象死去,而活下来的对象就会逐渐放到老年代中去,这就是上面说的强分代假说。 但是实际中对象的死活不不仅仅是分...
  • 我们可能知道jvm调优,但是jvm调优到底是为了调整什么呢?或者说是优化什么?...咱们都知道只有当老年代满了的时候会触发系统的full gc线程来进行full gc,所以引出了这次的题目,到底满足什么样条件的对象才...
  • 1.躲过15次GC之后进入老年代 系统刚启动时,创建的各种各样的对象,都是分配在年轻代里。 随着慢慢系统跑着跑着,年轻代满了,就会出发Minor GC ,可能1%的少量存活对像转移到空着的Survivor区中 然后系统继续运行...
  • 通常情况下,对象主要分配在新生代的Eden区上,少数情况下也可能会直接分配在老年代中。 1.Eden区 Eden区位于Java堆的年轻代,是新对象分配内存的地方,由于堆是所有线程共享的,因此在堆上分配内存需要加锁。而Sun...
  • 在jvm堆内存中分为年轻代与老年代,年轻代又分为Eden区与Survivor区.新对象的创建会分配在年轻代,那么对象何时进入老年代呢?又有什么样的对象适合放在老年代呢? YoungGC与FullGC简单区别 YoungGC波及范围小(年轻代...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 141,406
精华内容 56,562
关键字:

老年代

友情链接: easypermissions-master.zip