• JVM垃圾回收


    一、垃圾回收判断

      引用计数法:每次引用+1,解引用-1,引用为0可被回收,解决不了循环引用问题,会造成内存泄漏

      可达性分析算法:被根对象直接或间接引用的对象都不能回收

    Q:那些对象可以作为GC Root?

      Memory Analyzer工具可以作为堆内存分析工具,可以找到作为GC Root的对象

      抓取内存快照jmap -dump:format-b,live,file-1.bin pid

      a、System class 系统核心类,Object类,String类,HashMap等等

      b、Native Stack 操作引用的

      c、Thread 活动线程使用的对象,例如被局部变量引用的对象

      d、Business Monitor  被加锁的对象

    二、四种引用

      1、强引用 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

      2、软引用(SoftReference) 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用 对象 可以配合引用队列来释放软引用自身

      3、弱引用(WeakReference) 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象 可以配合引用队列来释放弱引用自身

      4、虚引用(PhantomReference) 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队, 由 Reference Handler 线程调用虚引用相关方法释放直接内存

      5、终结器引用(FinalReference) 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象

    例如:创建ByteBuffer的时候会创建一个名为Cleaner的虚引用对象,当ByteBuffer没有被强引用所引用就会被jvm垃圾回收,虚引用Cleaner就会进入引用队列,会有专门的线程扫描引用队列,被发现后会调用直接内存地址的方法将直接内存释放掉,保证直接内存不会导致内存泄漏

    是所有引用类型中最弱的,一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例。

      终结器引用:创建的时候会关联一个引用队列,当A4对象没有被强引用所引用时,A4被垃圾回收的时候,会将终结器引用放入到一个引用队列(被引用对象暂时还没有被垃圾回收),有专门的线程(优先级较低,可能会造成对象迟迟不被回收)扫描引用队列并调用finallize()方法,第二次GC的时候才能回收掉被引用对象

    三、垃圾回收算法

      标记清除:速度快,但会产生内存碎片

      标记整理:解决内存碎片问题,但效率低,牵扯到内存地址的变动

      复制:两块区域FROM和TO,首先标记,再进行复制从FROM到TO,最后进行交换,不会产生碎片问题,但会产生两块区域浪费内存空间

      分代垃圾回收机制:新生代(eden,s1,s2),老年代

    新的对象先被放入eden区,如果eden内存空间不够,会进行一次Minor GC,先标记出不能回收的对像,复制到TO区,对象年龄+1,再与FROM交换位置,

    当年龄大于15的时候就会被移到老年代,当老年代内存空间不足的时候,会先进行至少一次Minor GC,如果内存还是不足则会进行一次Full GC,此时如果内存还是不足则会产生内存溢出。

    GC会产生Stop The World,根据老年代的特点采用标记-整理-清除算法。

     四、垃圾回收器

    1、串行:单线程回收 

      单线程

      堆内存较小,适合个人使用

      -XX:+UseSerialGC

    2、吞吐量优先:基于标记-整理的算法来进行垃圾回收,运行到一个节点(安全点),STW所有线程(一般就是核心数)一起进行垃圾回收,cpu在这时会突然飙高到100%

      多线程

      堆内存较大,多核cpu

      让单位时间内,STW的时间最短

      并行的执行,会STW

      -XX:+UseParallelGC 作用在新生代 

      -XX:+UseParallelOldGC 作用在老年代

      -XX:UseAdaptiveSizePolicy 设置此项以后,并行收集器会自动选择年轻代大小和相应的Surivior区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时一直打开。

      -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) n一般设置19  一百分钟内允许五分钟的暂停

      -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值

      -XX:ParallelGCThreads=n:设置并行收集器并行收集时,使用的线程数,一般等于CPU数

    3、响应时间优先(CMS主要作用于老年代):基于标记-清除的算法并发(减少STW的时间)的进行垃圾回收,首先也是到达一个安全点,进行初始标记(STW,标记根对象),然后是一个并发标记(不会STW),

    重新标记(STW,因为在并发标记的时候,用户线程也在执行),最后并发的清理。对cpu的占有率相对较低,响应时间降低了,吞吐量上升了一点。当内存碎片化较多的时候,不能够存储新的对象的时候,

    会退化成单线程垃圾回收器,进行一次标记整理。

      多线程

      堆内存较大,多核cpu

      尽可能让单次STW的时间最短

      并发的执行,不会停掉用户线程(只是其中一些阶段)

      -XX:+UseParNewGC 作用在新生代 (基于标记-复制算法)

      -XX:+UseConcMarkSweepGC 作用在老年代 ,有时候并发失败会退化到-SerialOld(串行化)

      -XX:ParallelGCThreads=n:并行收集线程数

      -XX:ConcGCThreads=n:设置并发收集器收集时使用的线程数,建议并行收集线程数的1/4

      -XX:CMSFULLGCsBeforCompaction=n:由于并发收集器不对内粗空间进行压缩、整理,所以运行一段时间会产生“碎片”,使得运行效率低。此值设置运行n次GC以后对内训空间进行压缩、整理。

      -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。

      -XX:CMSInitiatingOccupancyFraction=70:当老年代内存使用达到n%,开始回收。CMSInitiatingOccupancyFraction = (100 - MinHeapFreeRatio) + (CMSTriggerRatio * MinHeapFreeRatio / 100)`

      -XX:+CMSScavengeBeforeRemark:重新标记之前先进行一次ygc

    4、Garbage First(G1)

                         G1 垃圾回收阶段

      同时注重吞吐量和低延迟,并发的执行

      适合超大内存,划分多个相等的区域

      整体上使用的标记整理算法,两个区域之间使用的复制算法

    配置参数参考:https://blog.csdn.net/megustas_jjc/article/details/105470675

    java9之前,-XX:+UseG1GC,java9之后默认是G1

     -XX:MaxGCPauseMillis=n:设置最大GC 暂停时间。这是一个大概值,JVM 会尽可能的满足此值

    -XX:G1HeapRegionSize=n:使用G1,Java堆被划分为大小均匀的区域。这个参数配置各个子区域的大小。此参数的默认值根据堆大小的人工进行确定。最小值为 1Mb 且最大值为 32Mb。

    回收阶段:https://www.jianshu.com/p/989429f646af

    Q:新生代垃圾回收,跨代引用问题?

      采用以卡表与Remembered Set的方法解决,https://blog.csdn.net/weixin_49193222/article/details/111313505

    Q:Remark

    • 黑色:表示根对象,或者该对象与它引用的对象都已经被扫描过了。
    • 灰色:该对象本身已经被标记,但是它引用的对象还没有扫描完。
    • 白色:未被扫描的对象,如果扫描完所有对象之后,最终为白色的为不可达对象,也就是垃圾对象。

    jdk8u20-XX:+UseStringDeduplication字符串去重功能(会占用cpu时间)

    jdk8u40-XX:+ClassUnloadingWithConcurrentMark 在并发标记阶段结束后,JVM就进行类卸载

    jdk8u60 回收巨型对象:不会进行copy,回收时优先考虑,会跟踪老年代所有的incoming引用,这样老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时被回收

    jdk9对G1的优化:

      并发标记必须在堆空间占满前完成,否则退化为fullGC

      jdk9之前需要使用XX:InitiatingHeapOccupancyPercent=n(启动并发GC周期时的堆内存占用百分比. G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比. 值为 0 则表示"一直执行GC循环". 默认值为 45.)设置,

      jdk9之后,可以动态调整这个值,会添加一个安全的空挡空间

    五、JVM调优

      1、官网查看JVM参数

      2、java –XX:+PrintFlagsFinal -versio | findStr "GC" 查看GC配置情况

      3、内存

      4、锁竞争

      5、cpu

      6、吞吐量和响应时间的取舍

      7、ZGC是从JDK11中引入的一种新的支持弹性伸缩和低延迟垃圾收集器

      8、另一种虚拟机Zing

    六、GC调优

      1、新生代内存调优

      Q:新生代越大越好吗?

        否,新生代太小,会频繁的发生minor gc,太大发生GC的时间会延后,并且会占用老年代的空间,使得发生full GC的几率变高,官网建议25%网上,50%往下,实际配置时要进行权衡

      Eden区:能容纳并发量*(请求-响应)的数据

      幸存区:大到能保存【当前活跃对象+需要晋升对象】,晋升阈值调配要得当

      2、老年代的内存调优

        a、越大越好

        b、先尝试不做调优,如果没有full GC,可以先尝试调优新生代

        c、观察发生full GC时 老年代内存占用,响应调大1/4-1/3

        d、调整老年代占用达到的阈值为75%-80%

      

      

  • 相关阅读:
    code of C/C++(2)
    code of C/C++ (1)
    dll 的编写和使用
    Python基础练习-数据类型与变量part2
    Python基础练习-数据类型与变量
    python基础练习-循环
    Linux grep
    nginx反向代理
    正则表达式
    Linux samba ing
  • 原文地址:https://www.cnblogs.com/sglx/p/15215993.html
Copyright © 2020-2023  润新知