JVM的垃圾回收算法及GC详解

一、JVM的垃圾回收算法 GC(Garbage Collector)垃圾回收器
  • 1.复制算法(新生代垃圾回收算法)

    • ①概述:把新生代内存划分为两块内存区域,然后只使用其中一块内存,待那块内存快满的时候,就把
      里面的存活对象一次性转移到另外一块内存区域,保证没有内存碎片,接着一次性回收原来的那块内存
      区域,再次空出来一块内存区域,两块内存区域重复循环使用
    • ②触发时机:分配新对象时,新生代内存空间不足
    • ③缺点:对内存的使用率太低(只有一半内存可以用)
    • ④优化:1个Eden区(80%),两个Survivor区(10%)
      新生对象放到Eden区,垃圾回收时把存活对象放到Survivor区,三区轮转
  • 2.标记整理算法(老年代垃圾回收算法)

    • ①触发时机:
      • 1)在Minor GC之前,检查老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC
      • 2)开启空间担保参数,老年代可用内存小于历代新生代GC后进入老年代的平均对象大小
      • 3)在Minor GC之后,存活对象大于Survivor,就会进入老年代,此时老年代内存不足。
      • 4)–XX:CMSInitiationOccupancyFaction 参数超标
    • ②概述:标记出来老年代当前存活的对象,挪到一边,紧凑靠在一起,然后再一次性把垃圾对象都回收掉
  • 3.小结:

    • 所谓JVM优化,就是尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁
    • 对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收。
    • 即不断优化垃圾回收器的机制和算法,降低垃圾回收
  • 4.优化方案:

    • ①调整新生代的内存比例,避免新生代对象直接进入老年代
    • ②调整-XX:SurvivorRatio=8,降低Eden区的比例,给Survivor区更多的空间
二、JVM的痛点:Stop the World
  • 1.概述:在垃圾回收的时候,JVM在后台直接进入Stop the World状态,即直接停止我们写的Java系统的所有工作线程
三、垃圾回收器
  • 1.ParNew(新生代垃圾回收器)
    • ①优点:多线程垃圾回收机制 (Serial垃圾回收期是单线程)
      • 垃圾回收会把系统程序的工作线程全部停掉,用多个垃圾回收线程回收垃圾。
    • 1.1.如何为线上系统指定使用ParNew垃圾回收器?
      • ①IDEA中可以设置Debug JVM Arguments
      • ②使用java -jar命令启动直接在后面接JVM参数即可
      • ③部署到Tomcat时可以在catalina.sh中设置JVM参数
        • 参数:-XX:+UserParNewGC
        • 默认垃圾回收线程数:与CPU核数一致 可以通过-XX:ParallelGCThreads调节
  • 2.CMS(老年代垃圾回收器)concurrent mark-sweep
    • ①概述:垃圾回收线程和系统工作线程尽量同时执行的模式处理
    • ②步骤:
      • 1)初始标记
        Stop 标记出来所有GC Roots直接引用的对象
        补充:方法的局部变量和类的静态变量是GC Roots,但是类的实例变量不是GC Roots
      • 2)并发标记
        • ①对已有对象进行GC Roots追踪
        • ②跟系统程序并发运行
      • 3)重新标记
        • Stop 重新标记第二阶段里新创建的对象和一些失去引用的垃圾
      • 4)并发清理(跟系统并发运行)
    • 2.1.CMS并发回收机制的缺点:
      • ①消耗CPU资源(CMS默认启动垃圾回收线程数量是(CPU核数)+3/4)
      • ②Concurrent Mode Failure问题
        • 1)问题:并发清理阶段,系统一直运行,可能让一些对象进入老年代,同时变成垃圾对象,即“浮动对象”
        • 2)解决:调整CMS垃圾回收触发时机中当老年代内存达到一定比例,自动执行GC
          –XX:CMSInitiationOccupancyFaction jdk1.6默认是92%
        • 3)异常:发生Concurrent Mode Failure会调用Serial Old 垃圾回收器代替CMS,直接Stop
    • 2.2.内存碎片问题
      • ①碎片整理:-XX:+UseCMSCompactAtFullCollection (默认开启)
        • Full GC之后要进行Stop,碎片整理挪到一起
  • 3.老年代Full GC比新生代Minor GC慢的原因
    • ①新生代直接从GC Roots出发追踪哪些对象是活的,存活对象少,速度快。一次性回收Eden区和Survivor区
    • ②CMS的Full GC在并发标记阶段,追踪所有存活对象,老年代存活对象多,速度慢。
      • 并发清理阶段,不是一次性回收,零散回收,速度慢
      • 内存整理,Stop。引发Concurrent Mode Failure需要用Serial Old
四、内存分配
  • 1.-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M
五、G1垃圾回收器
  • 1.核心设计思路:

    • 通过把内存拆分为大量小Region,以及追踪每个Region中可以回收的对象大小和预估时间
    • 在垃圾回收的时候,尽量把垃圾回收对系统造成的影响控制在指定时间范围内,同时在有限时间内尽量回收尽可能多的垃圾对象。
  • 2.特点:

    • ①把Java堆内存拆分为多个大小相等的Region
    • ②Region可能属于新生代也可能属于老年代
    • ③设置一个垃圾回收的预期停顿时间(Stop the World即系统停顿时间)
      • 1)如何对垃圾回收导致系统停顿可控?
        • :追踪每个Region的回收价值(回收时间及垃圾大小)
  • 3.G1对应的内存分配

    • ①使用 -XX:+UseG1GC 指定G1垃圾回收器,会用堆内存大小除以2048(JVM最多有2048个Region)
    • ②开始 默认新生代对堆内存的占比是5%,可通过 -XX:G1NewSizePercent 设置
    • ③运行 JVM不停给新生代增加Region,新生代占比最多不超过60%。 -XX:G1MaxNewSizePercent
    • ④G1新生代还有Eden和Survivor概念,占据不同的Region
  • 4.G1的新生代垃圾回收

    • ①相同:与ParNew垃圾回收类似,Eden区满进行回收
    • ②不同:G1可以设定目标GC停顿时间 -XX:MaxGCPauseMills 默认200ms
    • ③新生代进入老年代:
      • 1)对象躲过多次垃圾回收,达到一定年龄 -XX:MaxTenuringThreshold
      • 2)动态年龄判断,即新生代GC过后,存活对象超过Survivor的50%
  • 5.大对象Region

    • ①判定:超过一个Region大小的50%
    • ②触发时机:新生代、老年代回收时,顺带带着大对象Region一起回收
  • 6.新生代+老年代 垃圾混合回收(Mixed GC)

    • ①触发时机: 老年代占据堆内存的45%的Region时
      -XX:InitiatingHeapOccupancyPercen

    • ②回收过程:

      • 1)初始标记 Stop 仅标记GC Roots直接能引用的对象
      • 2)并发标记 GC Roots追踪
      • 3)最终标记 Stop
      • 4)混合回收 Stop
        • 注意:此时回收老年代、新生代和大对象(根据GC停顿时间挑选部分Region)
          最后一个阶段可执行多次混合回收 -XX:G1MixedGCCountTarget
    • ③G1整体是基于复制算法进行Region垃圾回收的

      • -XX:G1HeapWasterPercent 5%(空闲Region数量达到堆内存的5%,停止混合回收)
      • -XX:G1MixedGCLiveThresholdPercent 85%(存活对象低于85%的Region才回收)
  • 7.新生代GC优化

    • ①给JVM的堆区域足够的内存
    • ②合理设置 -XX:MaxGCPauseMills 参数
  • 8.Mixed GC优化

    • ①核心:尽量避免对象过快进入老年代,避免频繁触发mixed gc
  • 9.G1非常适合内存很大的机器

    • 因为内存很大,如果不用G1,会导致新生代每次GC回收垃圾太多,停顿时间太长,G1可以指定每次停顿时间
  • 10.如何打印出JVM GC日志?*************

    • -XX:+PrintGCDetails 打印详细的gc日志
    • -XX:+PrintGCTimeStamps 打印每次GC发生的时间
    • -Xloggc:gc.log 将gc日志写入一个磁盘文件
  • 11.开发工具指定JVM参数运行程序

    • 添加VM arguments
      在目录下生成gc.log

jdk自带命令行工具
六、jstat
  • 1.使用:
    jstat -gc PID 对应pid的java进程的内存和GC情况
  • 2.其他命令
    jstat -gccapacity PId: 堆内存分析
    jstat -gcnew PID:年轻代GC分析
七、jmap
  • 1.jmap -heap PID 堆内存相关
  • 2.jmap -histo PID 对象占用内存大小降序
  • 3.jmap -dump:live,format=b,file=dump.hprof PID 生成堆内存转储快照
八、jhat
  • jhat内置web服务器,支持通过浏览器以图形化方式分析堆转储快照
  • 1.jhat dump.hprof -port 7000
九、对线上系统进行JVM监控
  • 1.方法一:每天在高峰和低峰期使用jstat、jmap、jhat等工具查看
  • 2.监控系统:Zabbix、OpenFalcon、Ganglia

十、内存溢出
  • 1.Metaspace区域的内存溢出

    • ①设置大小:-XX:MetaspaceSize -XX:MaxMetaspace
    • ②原因:
      • 1)上线系统对Metaspace区域使用默认参数,内存过小
      • 2)使用动态代理技术生成一些类,导致类过多
  • 2.JVM栈内存溢出

    • ①一个线程调用多个方法的入栈和出栈
    • ②每次方法调用的栈帧都是占用内存的
    • ③原因:递归调用方法
  • 3.堆内存溢出

    • ①原因:有限的内存放过多对象,且大多数是存活的。
    • ②场景:
      • 1)系统承载高并发请求
      • 2)系统有内存泄漏的问题
  • 4.如何对线上系统的OOM异常进行监控和报警

    • ①监控系统
    • ②被动等待系统挂掉
  • 5.JVM内存溢出时,自动dump一份内存快照

    • ①-XX:+HeapDumpOnOutOfMemoryError //OOM时自动dump内存快照
    • ②-XX:HeapDumpPath=/usr/local/app/oom 快照位置
  • 6.JVM参数模板

    • “-Xms4096M -Xmx4096M -Xmn3072M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -XX:+PrintGCDetails -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/app/oom”

  • 补充:
    • ①Tomcat本身就是一个JVM进程,tomcat是用java写的
    • ②我们写好的类被tomcat加载到内存中,由tomcat执行所写的类
    • ③Tomcat有自己的工作线程,几百个
    • http-nio-8080-exec-1089 :tomcat的工作线程,负责调用代码运行时堆内存不够

  • 7.Jetty服务器的NIO机制导致堆外内存溢出

    • ①Direct buffer memory 堆外内存(操作系统直接管理)
    • ②Java代码里申请一块堆外内存:
      • 使用DirectByteBuffer这个类,构建一个对象在JVM堆内存中,堆外内存会划出来一块内存跟这个对象关联
    • ③最终优化:
      • 1)合理分配内存,给年轻代更多内存,让Survivor区域有更大的空间,避免DirectByteBuffer对象进入老年代,导致堆外内存释放不掉。
      • 2)放开-XX:+DisableExplicitGC限制,让System.gc()生效。
  • 8.微服务架构RPC调用引发的OOM

    • ①对象序列化和反序列化
    • ②序列化失败重新开辟数组保存字节数组(不能过大)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值