在这里插入图片描述
弱引用演示

public class Code_09_WeakReferenceTest {

    public static void main(String[] args) {
//        method1();
        method2();
    }

    public static int _4MB = 4 * 1024 *1024;

    // 演示 弱引用
    public static void method1() {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for(int i = 0; i < 10; i++) {
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB]);
            list.add(weakReference);

            for(WeakReference<byte[]> wake : list) {
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
    }

    // 演示 弱引用搭配 引用队列
    public static void method2() {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for(int i = 0; i < 9; i++) {
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB], queue);
            list.add(weakReference);
            for(WeakReference<byte[]> wake : list) {
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
        System.out.println("===========================================");
        Reference<? extends byte[]> poll = queue.poll();
        while (poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }
        for(WeakReference<byte[]> wake : list) {
            System.out.print(wake.get() + ",");
        }
    }

}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">

2、垃圾回收算法

1)标记清除

定义:Mark Sweep

2)标记整理

Mark Compact

3)复制

Copy

3、分代垃圾回收

在这里插入图片描述

1)相关 JVM 参数

class="table-box">
含义参数
堆初始大小-Xms
堆最大大小-Xmx 或 -XX:MaxHeapSize=size
新生代大小-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态)-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例-XX:SurvivorRatio=ratio
晋升阈值-XX:MaxTenuringThreshold=threshold
晋升详情-XX:+PrintTenuringDistribution
GC详情-XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC-XX:+ScavengeBeforeFullGC

2)GC 分析

public class Code_10_GCTest {

    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;

    // -Xms20m -Xmx20m -Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        list.add(new byte[_6MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_6MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_6MB]);
    }

}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

通过上面的代码,给 list 分配内存,来观察 新生代和老年代的情况,什么时候触发 minor gc,什么时候触发 full gc 等情况,使用前需要设置 jvm 参数。

4、垃圾回收器

相关概念:

1)串行

在这里插入图片描述

-XX:+UseSerialGC=serial + serialOld
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

安全点:让其他线程都在这个点停下来,以免垃圾回收时移动对象地址,使得其他线程找不到被移动的对象
因为是串行的,所以只有一个垃圾回收线程。且在该线程执行回收工作时,其他线程进入阻塞状态

Serial 收集器
Serial 收集器是最基本的、发展历史最悠久的收集器
特点:单线程、简单高效(与其他收集器的单线程相比),采用复制算法。对于限定单个 CPU 的环境来说,Serial 收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)!

ParNew 收集器
ParNew 收集器其实就是 Serial 收集器的多线程版本
特点:多线程、ParNew 收集器默认开启的收集线程数与CPU的数量相同,在 CPU 非常多的环境中,可以使用 -XX:ParallelGCThreads 参数来限制垃圾收集的线程数。和 Serial 收集器一样存在 Stop The World 问题

Serial Old 收集器
Serial Old 是 Serial 收集器的老年代版本
特点:同样是单线程收集器,采用标记-整理算法

2)吞吐量优先

在这里插入图片描述

-XX:+UseParallelGC ~ -XX:+UsePrallerOldGC
-XX:+UseAdaptiveSizePolicy
-XX:GCTimeRatio=ratio // 1/(1+radio)
-XX:MaxGCPauseMillis=ms // 200ms
-XX:ParallelGCThreads=n
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

Parallel Scavenge 收集器
与吞吐量关系密切,故也称为吞吐量优先收集器
特点:属于新生代收集器也是采用复制算法的收集器(用到了新生代的幸存区),又是并行的多线程收集器(与 ParNew 收集器类似)

该收集器的目标是达到一个可控制的吞吐量。还有一个值得关注的点是:GC自适应调节策略(与 ParNew 收集器最重要的一个区别)

GC自适应调节策略:
Parallel Scavenge 收集器可设置 -XX:+UseAdptiveSizePolicy 参数。
当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden 与 Survivor 区的比例(-XX:SurvivorRation)、
晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为 GC 的自适应调节策略。

Parallel Scavenge 收集器使用两个参数控制吞吐量:

Parallel Old 收集器
是 Parallel Scavenge 收集器的老年代版本
特点:多线程,采用标记-整理算法(老年代没有幸存区)

3)响应时间优先

在这里插入图片描述

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
-XX:CMSInitiatingOccupancyFraction=percent
-XX:+CMSScavengeBeforeRemark
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

CMS 收集器
Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的老年代收集器
特点:基于标记-清除算法实现。并发收集、低停顿,但是会产生内存碎片
应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如 web 程序、b/s 服务
CMS 收集器的运行过程分为下列4步:
初始标记:标记 GC Roots 能直接到的对象。速度很快但是仍存在 Stop The World 问题。
并发标记:进行 GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行。
重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在 Stop The World 问题
并发清除:对标记的对象进行清除回收,清除的过程中,可能任然会有新的垃圾产生,这些垃圾就叫浮动垃圾,如果当用户需要存入一个很大的对象时,新生代放不下去,老年代由于浮动垃圾过多,就会退化为 serial Old 收集器,将老年代垃圾进行标记-整理,当然这也是很耗费时间的!

CMS 收集器的内存回收过程是与用户线程一起并发执行的,可以搭配 ParNew 收集器(多线程,新生代,复制算法)与 Serial Old 收集器(单线程,老年代,标记-整理算法)使用。

4)G1 收集器

定义: Garbage First
适用场景:

相关参数:
JDK8 并不是默认开启的,所需要参数开启

-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
G1 垃圾回收阶段

在这里插入图片描述
Young Collection:对新生代垃圾收集
Young Collection + Concurrent Mark:如果老年代内存到达一定的阈值了,新生代垃圾收集同时会执行一些并发的标记。
Mixed Collection:会对新生代 + 老年代 + 幸存区等进行混合收集,然后收集结束,会重新进入新生代收集。

Young Collection

新生代存在 STW:
分代是按对象的生命周期划分,分区则是将堆空间划分连续几个不同小区间,每一个小区间独立回收,可以控制一次回收多少个小区间,方便控制 GC 产生的停顿时间!
E:eden,S:幸存区,O:老年代
新生代收集会产生 STW !
在这里插入图片描述

Young Collection + CM

在 Young GC 时会进行 GC Root 的初始化标记
老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由下面的 JVM 参数决定 -XX:InitiatingHeapOccupancyPercent=percent (默认45%)
在这里插入图片描述

Mixed Collection

会对 E S O 进行全面的回收

-XX:MaxGCPauseMills=xxms 用于指定最长的停顿时间!
问:为什么有的老年代被拷贝了,有的没拷贝?
因为指定了最大停顿时间,如果对所有老年代都进行回收,耗时可能过高。为了保证时间不超过设定的停顿时间,会回收最有价值的老年代(回收后,能够得到更多内存)
在这里插入图片描述

Full GC

G1 在老年代内存不足时(老年代所占内存超过阈值)
如果垃圾产生速度慢于垃圾回收速度,不会触发 Full GC,还是并发地进行清理
如果垃圾产生速度快于垃圾回收速度,便会触发 Full GC,然后退化成 serial Old 收集器串行的收集,就会导致停顿的时候长。

Young Collection 跨代引用
Remark

重新标记阶段
在垃圾回收时,收集器处理对象的过程中

但是在并发标记过程中,有可能 A 被处理了以后未引用 C ,但该处理过程还未结束,在处理过程结束之前 A 引用了 C ,这时就会用到 remark 。
过程如下

在这里插入图片描述
在这里插入图片描述

JDK 8u20 字符串去重

过程

优点与缺点

-XX:+UseStringDeduplication
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
JDK 8u40 并发标记类卸载

在并发标记阶段结束以后,就能知道哪些类不再被使用。如果一个类加载器的所有类都不在使用,则卸载它所加载的所有类

JDK 8u60 回收巨型对象

在这里插入图片描述

JDK 9 并发标记起始时间的调整

5、垃圾回收调优

查看虚拟机参数命令

D:\JavaJDK1.8\bin\java  -XX:+PrintFlagsFinal -version | findstr "GC"
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

可以根据参数去查询具体的信息

1)调优领域

2)确定目标

低延迟/高吞吐量? 选择合适的GC

3)最快的 GC

首先排除减少因为自身编写的代码而引发的内存问题

4)新生代调优

-XX:MaxTenuringThreshold=threshold
-XX:+PrintTenuringDistrubution
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

5)老年代调优

以 CMS 为例:

-XX:CMSInitiatingOccupancyFraction=percent
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

6)案例

案例1:Full GC 和 Minor GC 频繁
案例2:请求高峰期发生 Full GC,单次暂停时间特别长(CMS)
案例3:老年代充裕情况下,发生 Full GC(jdk1.7

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/weixin_50280576/article/details/113775575","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的CodeAli的文章"https://blog.csdn.net/weixin_50280576/article/details/113775575"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!