JVM(二)——垃圾回收

news2024/11/13 15:56:24

三、垃圾回收

1、如何判断对象可以回收

1)引用计数法

定义:
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
但是存在循环引用的问题:
在这里插入图片描述

2)可达性分析算法

一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过
程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,
或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的
    参数、局部变量、临时变量等
  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如
    NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

3)四种引用

在这里插入图片描述

  1. 强引用:强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object
    obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

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

  3. 弱引用 (WeakReference):在垃圾回收时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
    可以配合引用队列来释放弱引用自身

  4. 虚引用 (PhantomReference):无法通过虚引用获取一个对象实例。必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

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

软引用的例子:
设置堆内存大小为 20m,直接list.add(byte[]),会报 java.lang.OutOfMemoryError: Java heap space 异常,因为是强引用,无法垃圾回收。
使用 SoftReference 可以添加到 list 集合中,不会报错,但是执行到内存不足时,会进行垃圾回收。

/**
 * 演示软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class Demo2_3 {
    private static final int _4MB = 4 * 1024 * 1024;
    
    public static void main(String[] args) throws IOException {
        /*List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new byte[_4MB]);
        }

        System.in.read();*/
        soft();
    }

    public static void soft() {
        // list --> SoftReference --> byte[]
        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }
        
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());	// null
        }
    }
}

在这里插入图片描述
输出结果中软引用被为null,可以通过引用队列释放软引用自身

/**
 * 演示软引用, 配合引用队列
 */
public class Demo2_4 {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }
        
    }
}

在这里插入图片描述

弱引用的例子:

/**
 * 演示弱引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class Demo2_5 {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        //  list --> WeakReference --> byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
            list.add(ref);
            for (WeakReference<byte[]> w : list) {
                System.out.print(w.get()+" ");
            }
            System.out.println();
        }
        System.out.println("循环结束:" + list.size());
    }
}

2、垃圾回收算法

1)标记清除(Mark-Sweep)

  • 速度较快
  • 会造成内存碎片

在这里插入图片描述

2)标记整理(Mark-Compact)

  • 速度慢
  • 没有内存碎片

在这里插入图片描述

3)复制(Copy)

  • 不会有内存碎片
  • 需要占用两倍内存空间

在这里插入图片描述

3、分代垃圾回收

在这里插入图片描述

老年代:常时间使用的对象,垃圾回收很久发生一次。单线程的垃圾收集器Serial Old(串行),多线程并发Parallel Old(吞吐量优先)使用的垃圾回收方法是 标记-整理,多线程并发的垃圾收集器CMS(Concurrent Mark Sweep)(响应时间优先)使用的垃圾回收方法是 标记-清除

新生代:经常需要更换的对象,垃圾回收频繁,"朝生夕灭"的特点。垃圾收集器(Serial)使用的方法是 复制

  • 对象首先分配在伊甸园区域
  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的
    对象年龄加 1并且交换 from to
  • minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长

1) 相关JVM参数

在这里插入图片描述

4、垃圾回收器

  1. 串行
    • 单线程
    • 堆内存较小,适合个人电脑
  2. 吞吐量优先
    • 多线程
    • 堆内存较大,多核 cpu
    • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高
  3. 响应时间优先
    • 多线程
    • 堆内存较大,多核 cpu
    • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

1)串行

-XX:+UseSerialGC = Serial + SerialOld
在这里插入图片描述

Serial收集器:它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强
调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器,有着优于其他收集器的地方,那就是简单而高效(与其他收集器的单线程相比),对于内存资源受限的环境,它是所有收集器里额外内存消耗(Memory Footprint)最小的。

2)吞吐量优先

-XX:+UseParallelGC ~ -XX:+UseParallelOldGC
-XX:+UseAdaptiveSizePolicy
-XX:GCTimeRatio=ratio
-XX:MaxGCPauseMillis=ms
-XX:ParallelGCThreads=n
在这里插入图片描述

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 运行垃圾收集时间)

Parallel Scavenge收集器:老年代的垃圾回收器,支持多线程并发收集,基于标记-整理算法实
现。

3)响应时间优先

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
-XX:CMSInitiatingOccupancyFraction=percent
-XX:+CMSScavengeBeforeRemark
在这里插入图片描述

CMS(Concurrent Mark Sweep) 收集器: 是一种低延迟、响应时间优先的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为
关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验。CMS收集器就非
常符合这类应用的需求。

5、G1 (Garbage First)

使用场景:

  • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200ms
  • 超大堆内存,会将堆划分为多个大小相等的 Region
  • 整体上是标记 + 整理算法,两个区域之间是复制算法
    相关 JVM 参数
    -XX:+UseG1GC
    -XX:G1HeapRegionSize=size
    -XX:MaxGCPauseMillis=time

1)G1 垃圾回收阶段

在这里插入图片描述

2)Young Collection

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

3)Young Collection + CM(并发标记)

  • 在 Young GC 时会进行 GC Root 的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

-XX:InitiatingHeapOccupancyPercent=percent (默认45%)
在这里插入图片描述

4)Mixed Collection

会对 E(伊甸园区)、S(幸存区)、O(老年代)进行全面垃圾回收

  • 最终标记(Remark)会 STW
  • 拷贝存活(Evacuation)会 STW

-XX:MaxGCPauseMillis=ms
在这里插入图片描述

5)Full GC

  • SerialGC
    • 新生代内存不足发生的垃圾收集 -minor gc
    • 老年代内存不足发生的垃圾 - full gc
  • ParallelGC
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足发生的垃圾收集 - full gc
  • CMS
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足
  • G1
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足

6)跨代引用

  • 新生代回收的跨代引用(老年代引用新生代)问题
    在这里插入图片描述
  • 卡表与 Remembered Set
  • 在引用变更时通过 post-write barrier + dirty card queue
  • concurrent refinement threads 更新 Remembered Set

在这里插入图片描述

7)Remark

  • pre-write barrier + satb_mark_queue

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

  • 黑色:已被处理,需要保留的
  • 灰色:正在处理中的
  • 白色:还未处理的
    在这里插入图片描述

6、垃圾调优

C:\Program Files\Java\jdk1.8.0_271\bin\java -XX:+PrintFlagsFinal -version | findstr "GC"
可以根据参数去查询具体的信息

1)调优领域

  • 内存
  • 锁竞争
  • cpu 占用
  • io
  • gc

2)确定目标

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

  • CMS,G1, ZGC
  • ParallelGC
  • Zing

3) 最快的 GC是不发生 GC

  • 查看 FullGC 前后的内存占用,考虑下面几个问题
    • 数据是不是太多?
      • resultSet = statement.executeQuery(“select * from 大表 limit n”)
    • 数据表示是否太臃肿?
      • 对象图
      • 对象大小 16 Integer 24 int 4
    • 是否存在内存泄漏?
      • static Map map =
      • 第三方缓存实现

4)新生代调优

  • 新生代的特点
    • 所有的 new 操作的内存分配非常廉价
      • TLAB thread-lcoal allocation buffer
    • 死亡对象的回收代价是零
    • 大部分对象用过即死
    • Minor GC 的时间远远低于 Full GC

一般对新生代调优,可以调优的空间大

  • 越大越好吗?
    -Xmn (设置堆空间中新生代的内存大小)
    Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). GC is
    performed in this region more often than in other regions. If the size for the young generation is
    too small, then a lot of minor garbage collections are performed. If the size is too large, then only
    full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.

回答:新生代的空间越大,吞吐量越大,(Full GC的次数少了),但是空间大到一定程度时,吞吐量会下降,因为空间太大,一旦触发 Full GC 时,垃圾回收时间会大大增加。

  • 新生代能容纳所有【并发量 * (请求-响应)】的数据
  • 幸存区大到能保留【当前活跃对象 + 需要晋升对象】
  • 晋升阈值配置得当,让长时间存活的对象尽快晋升
    -XX:MaxTenuringThreshold=threshold
    -XX:+PrintTenuringDistrubution
    

5)老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好
  • 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代
  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
    • -XX:CMSInitiatingOccupancyFraction=percent 值越低,老年代触发垃圾回收的时机越早

6)案例

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1543714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UFS DMA介绍

一. Linux DMA简介 我们知道DMA是Direct Memory Access, 不需要CPU的参与&#xff0c;也可以直接访问内存中的数据。 CPU 虚拟地址&#xff1a;内核空间由于有MMU内存管理&#xff0c;正常使用的是虚拟地址 CPU物理地址&#xff1a;virtual memory system (TLB, page tables,…

vscode中导入#include “opencv2/opencv.hpp“

鼠标放到上面 点击快速修复 1.img.cpp // 图片的读取和显示 // 导入opencv头文件 #include "opencv2/opencv.hpp" #include <iostream>int main(int argc, char** argv) {// 读取图片&#xff0c;mat是matrix的缩写&#xff0c;是一个矩阵&#xff0c;类似与n…

代码随想录算法训练营三刷 day34 | 贪心之1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

三刷day34 1005.K次取反后最大化的数组和134. 加油站135. 分发糖果 1005.K次取反后最大化的数组和 题目链接 解题思路&#xff1a; 两次贪心 如何可以让数组和最大呢&#xff1f; 局部最优&#xff1a;让绝对值大的负数变为正数&#xff0c;当前数值达到最大&#xff0c;整体最…

C++:引用的简单理解

前言&#xff1a;引用是C一个很重要的特性&#xff0c;最近看了很多有关引用的资料和博客&#xff0c;故在此对引用的相关知识进行总结 一、什么是引用 引用&#xff0c;顾名思义是某一个变量或对象的别名&#xff0c;对引用的操作与对其所绑定的变量或对象的操作完全等价 语…

MATLAB:优化与规划问题

一、线性规划 % 线性规划&#xff08;Linear programming, 简称LP&#xff09; fcoff -[75 120 90 105]; % 目标函数系数向量 A [9 4 7 54 5 6 105 10 8 53 8 9 77 6 4 8]; % 约束不等式系数矩阵 b [3600 2900 3000 2800 2200]; % 约束不等式右端向量 Aeq []; % 约束等式系…

短剧直播项目,一个信息赚3600+,小白也能轻松上手!

一位陌生朋友的一句话&#xff0c;我让他赚了3600&#xff0c;你敢相信吗&#xff1f; 事情是在上个礼拜六&#xff0c;一位朋友通过知乎添加我&#xff0c;问我短剧怎么授权直播。我说现在只能授权剪辑&#xff0c;没有授权直播的权限。于是他又问我&#xff0c;你们有教短剧…

Excel·VBA数组平均分组问题

看到一个帖子《excel吧-数据分组问题》&#xff0c;对一组数据分成4组&#xff0c;使每组的和值相近 上一篇文章《ExcelVBA数组分组问题》&#xff0c;解决了这个帖子问题的第1步&#xff0c;即获取所有数组分组形式的问题 接下来要获取分组和值最相近的一组&#xff0c;只需计…

2024年洗地机综合实力排行榜:谁才是真正的洗地神器?

近年来&#xff0c;洗地机在行业里&#xff0c;它集合了扫地和拖地以及自动清洁和除菌的功能&#xff0c;备受人们的喜爱&#xff0c;尤其是平时忙于工作并没有多少时间清洁家务的用户&#xff0c;但是对于第一次接触洗地机的用户来说&#xff0c;怎么选购洗地机也是个问题&…

2024新版本知识付费微信小程序源码_亲测可用(附下载链接)

介绍&#xff1a; 主要功能 会员系统&#xff0c;用户登录/注册购买记录 收藏记录 基本设置 后台控制导航颜色 字体颜色 标题等设置 流量主广告开关小程序广告显示隐藏 广告主审核过审核 资源管理 后台可以添加5种类型资源灵活设置 激励广告解锁资源 vip专享资源 免费资源…

代码随想录算法训练营第三十四天 | 1005.K次取反后最大化的数组和,134. 加油站 , 135. 分发糖果

贪心&#xff0c;每次选择最小的数来取反 用了一个优先级队列来做处理&#xff0c;维护一个顺序的数组 class Solution { public:typedef struct comp{bool operator()(const int & a,const int & b){return a > b;}}comp;int largestSumAfterKNegations(vector<…

JavaEE初阶——关于进程

此篇文章和大家一起分享关于操作系统对进程的调度 目录 1.操作系统 操作系统的构成 2.进程 2.1进程的概念 2.2管理 2.3pcb (1)pid (2)内存指针 ​编辑 (3)文件描述符 (4)进程调度信息 并行执行 并发执行 pcb中支持进程调度的属性 谢谢您的访问!!期待您的关注!! …

【概念验证(POC):技术项目开发的关键一步】

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

redis开启aof持久化失败,无法生成.aof文件

大概率是因为redis-server服务没有关&#xff0c;关掉之后先删除.rdb文件&#xff0c;然后重启server&#xff0c;再去使用cli&#xff0c;然后退出应该就有.aof文件了 1、改配置文件 在redis.conf文件中搜索appendonly&#xff0c;然后将no修改为yes&#xff0c;保存并退出。…

程序员必会技能—股权之舟引领创业者驶向成功之岸【文末送书-44】

文章目录 程序员必会技能—股权之舟引领创业者驶向成功之岸股权进阶&#xff1a;让股权助创业成功的核心3问【文末送书-44】 程序员必会技能—股权之舟引领创业者驶向成功之岸 在创业过程中&#xff0c;股权管理是至关重要的一环。正确处理股权关系可以促进公司的发展&#xf…

RuoYi-Vue-Plus(登录流程-验证码生成)

一、登录流程 1- 进入登录页面&#xff0c;调用 com.ruoyi.web.controller.common.CaptchaController 类中的 captchaImage 方法&#xff0c;生成base64的图片 以及 UUID 2- 提交 登录信息 验证码 uuid 比对 错误&#xff1a;返回错误信息&#xff0c;删除缓存的验证码 成功…

vivado Modifying Logic

修改逻辑 在中实现后&#xff0c;可以修改非只读逻辑对象的属性Vivado IDE以及Tcl。 注意&#xff1a;有关Tcl命令的更多信息&#xff0c;请参阅Vivado Design Suite Tcl命令参考指南&#xff08;UG835&#xff09;&#xff0c;或键入&#xff1c;command&#xff1e;-help。…

SEO 谷歌浏览器模拟baodu蜘蛛 模拟UA 设置UA

目录 前言baidu UA操作设置百度UA 前言 要在谷歌浏览器中设置用户代理&#xff08;User Agent&#xff09;来模拟百度蜘蛛&#xff0c;您可以按照以下步骤进行操作 baidu UA Mozilla/5.0 (compatible; Baiduspider-render/2.0; http://www.baidu.com/search/spider.html)操作…

Linux(1)常用指令总结大全

1、firewall-cmd 1.1 打开443/TCP端口 firewall-cmd --add-port443/tcp 1.2 永久打开3690/TCP端口 firewall-cmd --permanent --add-port3690/tcp ​ # 永久打开端口好像需要reload一下&#xff0c;临时打开好像不用&#xff0c;如果用了reload临时打开的端口就失效了 # …

【无标题】C高级325

练习1&#xff1a;输入一个数&#xff0c;实现倒叙123-》321 练习2&#xff1a;输入一个&#xff0c;判断是否是素数 练习3&#xff1a;输入一个文件名&#xff0c; 判断是否在家目录下存在, 如果是一个目录&#xff0c;则直接输出是目录下的sh文件的个数 如果存在则判断是否是…

java常用应用程序编程接口(API)——IO流概述及字节流的使用

前言&#xff1a; IO流和File是用于把数据存放在硬盘里的工具。File可以把文件存至硬盘&#xff0c;但不能更改里面的数据。通过IO流可以改写硬盘里的数据。整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! IO流 I指Input&#xff0c;称为输入流&#xff1a;负责把…