美文网首页
JVM垃圾回收

JVM垃圾回收

作者: 小明阿婆 | 来源:发表于2021-11-28 20:31 被阅读0次

    JVM (java虚拟机)

    参考文献:

    Java 虚拟机(JVM )- 内存与垃圾回收篇概述哔哩哔哩bilibili

    Java开发必须掌握的线上问题排查命令-HollisChuang's Blog

    JVM

    JVM:java虚拟机,执行JAVA字节码的虚拟计算机。

    是否遇到过这些问题?

    - 运行着的线上系统突然卡死,系统无法访问,甚至直接OOM
    - JVM GC问题
    - JVM参数
    



    所有java程序都运行在JVM上

    JVM特点

    • 代码一次编译,到处运行(字节码)

    • 自动内存管理

    • 自动垃圾回收功能

    • 一个JAVA程序进程对应一个JVM

    • JVM允许一个应用有多个线程并行的执行

    JVM的整体结构

    jvm整体架构图文详解等待、的博客-CSDN博客jvm架构





    堆 heap

    对进程独立,对线程为共享

    栈存放对象、指针,而堆空间存放实例

    • 对象、指针 指向堆中存放的实例。

    • 对象出栈时,堆中对应的内容不会回收;而是新的实例进入堆且堆放不下时,发生GC时,才进行回收。

    • GC的本质是:回收已经出栈了的对象或指针的实例。

    基于分代收集理论设计,堆空间分为:

    逻辑上分为:新生区+养老区+元空间(方法区)

    实际上的堆(heap)分为:新生区+养老区

    新生区具体细分为:

    Eden + Survivor1 或 Survivor2 (Survivor1与Survivor2只有一个存放信息)

    Eden + from 或 to

    各区域的默认比例与设置参数

    默认情况下:

    新生区 : 养老区 = 1/3:2/3

    Eden :S1 :S2 = 8/10:1/10:1/10

    堆空间大小的设置

    这里实际上指 新生区 + 养老区

    -Xms:用于表示堆区的起始内存 (-X:是jvm的运行参数;ms:是memory start)

    -Xmx:用于表示堆区的最大内存 (mx:是memory maximum)

    • 堆空间,默认单位为kb,但后面可以接k、m、G作为单位,如:-Xms 6144k;-Xms 6m;

    • 不设置堆空间大小时,默认初始内存大小为:主机内存/64,最大内存大小为:主机内存/4

    • 开发中建议将-Xms与-Xmx设置为一样,原因为:-Xms与-Xmx不一致时,内存不足时会进行扩容,内存充裕时会进行缩容,造成系统资源的不必要损耗。

    • 一旦堆区中的内存大小超过 '-Xmx',将会抛出OOM(outofmemory)

    查看堆使用率

    (一)方法一
    # 获取pid
    jps
    jstat -gc pid
    
    (二)方法二
    # 添加参数
    -XX:+PrintGCtails
    

    实例分配的过程与回收过程

    (1)新对象申请
    (2)Eden判断是否放得下
     Eden放得下,将新对象分配到Eden
     Eden放不下,发生一次YGC,YGC后再进行判断Eden是否放得下
     Eden放得下,将新对象分配到Eden
     超大对象,Eden放不下,判断Old是否放得下
     Old放得下,将新对象分配到Old
     Old放不下,发生一次FGC,FGC后再进行判断Old是否放得下
     Old放得下,将对象分配到Old
     Old放不下,发生outofmemory(OOM)
    
    YGC的动作:
    (1)YGC时,回收Eden与Servivor中没有对象的实例
     剩余的实例存放到空的S0/S1中,这种情况是S0/S1空间足够
     这个时候会判断对象是否age>阈值(默认15),是则直接放到养老区
     S0/S1空间不足时,将实例放到养老区
    
    FGC的动作:
    (1)YGC时,回收Old中没有对象的实例
    
    
    
    ## 特别需要注意的:
    (1)发生GC时,会导致用户线程(作业线程)暂停。
    (2)实例每在新生区移动一次,age+1
    

    MinorGC、MajorGC、FullGC的区别

    GC按照回收区域分为两大种类型:

    (1)部分收集(Partial GC)

    (2)整堆收集(Full GC)

    • MinorGC == Young GC,新生区收集,只针对Eden、S0、S1

    • MajorGC == Old GC,养生区收集,只针对Old

    • Full GC,整堆收集,收集整个java堆和方法区的垃圾收集

    新生区GC触发机制与特性

    • 触发机制:Eden空间不足,触发YGC

    • Minor GC触发很频繁,回收速度也较快

    • GC会引发STW,暂停用户线程(作业线程),等垃圾回收结束,用户线程才恢复运行

    养生区触发机制与特性

    特别的:目前,只有CMS收集器会有单独收集老年代的行为。其他收集器均无此行为。

    • 触发机制:Old空间不足,触发MajorGC(Old GC)



    Full GC触发机制与特性

    对整堆(新生代,养生代)和方法区的垃圾收集。

    触发机制:

    • 调用system.gc(),系统建议执行Full GC,但是不一定会执行;

    • 养生代空间不足;

    • 方法区空间不足;

    • 通过Minor GC后进入养生代的实例大于养生代的可用内存

    • S0/S1复制时,Survivor区空间不足,将实例转存到养生代,但实例大于养生代的可用内存

    总结

    • Minor GC是清理新生代,但Survivor区满时不会触发;

    • Major GC是清理养生代;

    • Full GC是清理整个堆和方法区,包括新生代,养生代,方法区。





    JAVA堆分代思想

    • 分代的唯一理由就是优化GC性能。

    • 若不进行分代,则每次GC都需要堆所有区域进行扫描。

    内存分配策略

    • 优先分配到Eden

    • 大对象直接分配到养生区

    • 长期存活的对象分配到养生区



    TLAB (给每个线程单独分配的缓冲区,私有)

    thread local allocation buffer

    优势:提高对象的创建效率

    为什么有TLAB

    • 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据

    • 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的

    • 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

    • 所以进入TLAB机制!!!

    什么是TLAB

    • 对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域

    • 一旦对象在TLAB空间分配内存失败时(实例过大,或TLAB写满等),JVM就会尝试通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存

    • TLAB默认开启

    • 默认下TLAB空间非常小,仅占整个Eden空间的1%

    参数:

    # TLAB的开与关
    -XX:+UseTLAB
    -XX:-UseTLAB
    
    # 设置TLAB空间所占用Eden空间的百分比大小
    -XX:TLABWasteTargetPercent=1
    

    堆空间的参数设置

    -Xms10m
    设置堆最小值为10M
    
    -Xmx10m
    设置堆最大值为10M
    
    -XX:SurvivorRatio=8
    年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1,一个Survivor区占整个年轻代的1/10
    
    -XX:NewRatio=3
    设置年轻代(EC+S0C+S1C)和年老代(OC)的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4(jdk1.8,默认2)
    
    -XX:+PrintGCDetails
    打印GC的具体信息
    
    
    -XX:loggc:D:/a.log
    将jvm的日志存储到指定文件
    
    #参考链接
    https://www.jianshu.com/p/bcdae3c80c1d
    
    

    相关文章

      网友评论

          本文标题:JVM垃圾回收

          本文链接:https://www.haomeiwen.com/subject/rtphxrtx.html