• JVM GC 机制


    一、Java堆内存结构
     
    JVM将堆分成了二个大区新生代(Young)和老年代(Old),新生代又被进一步划分为Eden(伊甸园空间)和Survivor(幸存者空间)区,而Survivor由Survivor1和Survivor2组成,也有些人喜欢用FromSpace和ToSpace来代替。这里为什么要将Young划分为Eden、Survivor1、Survivor2这三块,给出的解释是  
        “Young中的98%的对象都是死朝生夕死,所以将内存分为一块较大的Eden和两块较小的Survivor1、Survivor2,JVM默认分配是8:1:1,每次调用Eden和其中的Survivor1(FromSpace),当发生回收的时候,将Eden和Survivor1(FromSpace)存活的对象复制到Survivor2(ToSpace),然后直接清理掉Eden和Survivor1的空间。”

    1.Young Gen Space 空间三个区的内存比例为 eden : s1 : s2 = 8:1:1
    2.其中一个幸存者空间必须保持是空的。如果两个幸存者空间都有数据,或者两个空间都是空的,那一定标志着你的系统出现了某种错误
     
    新生代(Young generation): 绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可到达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为”minor GC“。
     
    老年代(Old generation): 对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,我们称之为”major GC“(或者”full GC“)
     
    永久代(PermanentGeneration),用来保存程序运行时长期存活的对象,如类的元数据。但是Java8中已经用meta space完全替代了永久代。jvm参数-XX:PermSize和-XX:MaxPermSize选项会被忽略。
    永久代是在堆内存    之中保存的,但是永久代不会被回收。例如intern()方法,如果永久代中的数据量过大,那么这个程序依然会抛出OutOfMemery异常。
     
    (1)对象在内存中移动过程:
    1.绝大多数刚刚被创建的对象会存放在伊甸园空间。
    2.在伊甸园空间执行了第一次GC之后,存活的对象被移动到其中一个幸存者空间。
    3.此后,在伊甸园空间执行GC之后,存活的对象会被堆积在同一个幸存者空间。
    4.当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。之后会清空已经饱和的那个幸存者空间。
    5.在以上的步骤中重复几次依然存活的对象,就会被移动到老年代。
     
    (2)对象分配规则:
    • 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
    • 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
    • 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,知道达到阀值对象进入老年区。
    • 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
    • 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。 
     
     
    二、Java 垃圾回收流程
     
     
    GC流程:
    1. 当我们new一个对象的时候,首先会在Eden空间申请一块内存空间;
    2. 如果Eden空间不足,则会执行一次MinorGC;在执行MinorGC的过程中,Eden区中存活的对象和其中一块Survivor存活的对象通过复制算法拷贝到另外一块空白的Survivor区中。并且在对象转移的过程中,某些对象到达一定的年龄(这个年龄是动态判断的,最大为15)就会进入到老年代;
    3. 如果这个时候空间还不充足,则会执行一次MajorGC,回收Old区;在执行MajorGC的过程是比较耗时的,并且会至少触发一次MinorGC(要腾出Eden区空间);
    4. 如果这个时候空间还不充足,则会执行一次FullGC(包含新生代、年老代、永久化的GC);
    5. 如果这个时候空间还不充足,则会抛出OOM异常。
     
    GC 类型:
    • Minor GC 针对新生代的 GC,Eden区满了之后会触发,Servivor区满了不会触发。
    • Major GC 针对老年代的 GC
    • Full GC 针对新生代、老年代、永久带的 GC。这个从GC打印日志也可以看出来
     
     
    MinorGC
    年轻代是所有新对象产生的地方。当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做MinorGC。年轻代被分为3个部分——Enden区和两个Survivor区。
    新对象在年轻代产生,当年轻代空间占满后,会触发MinorGC开始垃圾回收,大多数新建对象位于Eden区,Eden区满后触发一次MinorGC,存活下来的对象转到survivor0区,survivor0区满后触发执行MinorGC,survivor0区存活对象全部转入survivor1区,这样保证一段时间内总有一个survivor区为空。经过多次MinorGC仍然存活的对象会转入老年代,这是由设定的年龄阈值来决定的。
     
    MajorGC
    老年代空间存储长期存活的对象,老年代占满时会触发MajorGC,花费时间较长,由于垃圾回收会导致“stoptheworld”事件,即所有线程都会停下来等待垃圾回收执行完成,所以对响应要求高的应用应尽量减少发生MajorGC,如微博后台程序发生MajorGC就会导致前台页面刷新超时,若是股票等实时交易应用发生MajorGC导致响应超时,可能会给客户带来损失,这都是系统调优要考虑到的问题。
    在分代收集算法中,由于年轻代中对象的存活率低,通常使用复制算法进行垃圾回收,老年代一般使用”标记-清理”或”标记-整理”算法回收存活率较高的对象。
     
    Minor和Full GC有什么不一样?
    • 新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕死的特性,所以Minor GC非常频繁,一般回收速度也比较快。
    • 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现Major GC,经常会伴随至少一次的Minor GC(但并非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
     
     
    JDK1.8后与JDK1.8之前
    • Jdk1.6及之前:常量池分配在永久代 。
    • Jdk1.7:有,但已经逐步“去永久代” 。
    • Jdk1.8及之后:无(java.lang.OutOfMemoryError: PermGen space,这种错误将不会出现在JDK1.8中)。元空间使用的是直接内存。jdk1.8取消永久代的目的是为了将hotspot与jrockit两个虚拟机的标准联合为一个。
     
  • 相关阅读:
    Android APK安装过程介绍
    Android Linux自带iptables配置IP访问规则
    海康视频监控---Demo
    Go之获取Windows下文件是否隐藏
    Go之go与channel组合使用
    Go并发编程(四)
    Go错误处理(二)
    Go面向对象(三)
    判断两端日期是否重合
    Sql分页存储过程
  • 原文地址:https://www.cnblogs.com/caoxb/p/13139931.html
Copyright © 2020-2023  润新知