jvm学习笔记(二) ----- 垃圾回收

GC

  • 一、判定对象是否是垃圾
    • 1.引用计数法
    • 2.可达性分析算法
  • 二、垃圾回收算法
    • 1.标记清除
    • 2.标记整理
    • 3. 复制
    • 4. 分代垃圾回收
      • 1.尝试在伊甸园分配
      • 2.大对象直接晋升至老年代
      • 3.多次存活的对象
      • 4.老年代连续空间不足,触发 Full GC

链接: jvm学习笔记(一) ----- JAVA 内存
链接: jvm学习笔记(三) ----- 垃圾回收器

一、判定对象是否是垃圾

1.引用计数法

  • 有一个地方引用对象,计数加一,当计数为零表示可以回收
  • 缺点是难以解决对象之间的循环引用问题

2.可达性分析算法

  • java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象。它从一系列 GC Roots 出发,边标记边探索所有被引用的对象
  • 从 GC Root对象 为起点,看是否能沿着引用链找到该对象,找不到,表示可以回收
  • GC Root对象 包括栈帧中的局部变量、方法区中的静态变量、方法区中的常量、本地方法栈中JNI引用的对象
  • 为了防止在标记过程中堆栈的状态发生改变,Java 虚拟机采取安全点机制来实现 Stop-the-world (应用程序的线程全部停止) 操作,暂停其他非垃圾回收线程
  • 当然,安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java 虚拟机的堆栈不会发生变化。这么一来,垃圾回收器便能够“安全”地执行可达性分析

二、垃圾回收算法

1.标记清除

第一遍标记、第二遍收集。缺点是会产生内存碎片,碎片过多,仍会使得连续空间少
在这里插入图片描述

2.标记整理

第一遍标记、第二遍整理,整理是指存活对象向一端移动来减少内存碎片,相对效率较低
在这里插入图片描述

3. 复制

开辟两份大小相等空间,一份空间始终空着,垃圾回收时,将存活对象拷贝进入空闲空间,优点是不会有内存碎片,但占用空间多。
在这里插入图片描述

4. 分代垃圾回收

  1. 大部分的 Java 对象只存活一小段时间,而存活下来的小部分 Java 对象则会存活很长一段时间。
  2. 根据对象的特点分代(分区域)来进行,分为新生代和老年代,新生代对象一般很少存活,采用『复制算法』、老年代对象生存时间长,适合采用『标记-清除算法』或『标记-整理算法』
  3. 堆内存分为『新生代』和『老年代』,『新生代』又分为『伊甸园』和两个『幸存区』。新生代内存不足触发的 GC 称为 Minor GC ,暂停时间很短,老年代内存不足触发的 GC 称为 Full GC 暂停时间较长,一般是新生代 GC 的几十倍,它们使用的垃圾回收算法不同,见之前的介绍。

1.尝试在伊甸园分配

对象优先在『伊甸园』分配,当『伊甸园』没有足够的空间时,触发 Minor GC ,将『伊甸园』和『幸存区 From』中仍然存活的对象利用 复制算法 移入『幸存区 To』,然后交换『幸存区 From』和『幸存区 To』的位置。

默认情况下,Java 虚拟机采取的是一种动态分配的策略(对应 Java 虚拟机参数 -XX:+UsePSAdaptiveSurvivorSizePolicy),根据生成对象的速率,以及 Survivor区的使用情况动态调整 Eden 区和 Survivor 区的比例。当然,你也可以通过参数 -XX:SurvivorRatio 来固定这个比例。但是需要注意的是,其中一个 Survivor 区会一直为空,因此比例越低浪费的堆空间将越高。

情况1:伊甸园空间还够,新对象在伊甸园能够存储的下,这时候不会发生GC。图中白色区域是空闲空间、蓝色矩形表示已创建对象。
在这里插入图片描述
情况2:伊甸园空间不够了。

在这里插入图片描述
标记可回收的对象,图中用黄色表示,这时候会用户线程会被暂停(Stop The World)。

在这里插入图片描述
触发新生代的垃圾回收,称为 Minor GC ,幸存对象移入『幸存区 To』,注意这里用的是复制算法,因此在幸存区没有碎片。
在这里插入图片描述
最后的结果,注意 GC 完成后,From 和 To 交换了位置,另外幸存区的对象开始记录寿命。
在这里插入图片描述

2.大对象直接晋升至老年代

当对象太大,伊甸园包括幸存区都存放不下时,这时候老年代的连续空间足够,此对象会直接晋升至老年代,不会发生 GC

在这里插入图片描述
结果:
在这里插入图片描述
测试:

  1. 预先定义一组大小
    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;

在运行时添加如下 JVM 参数:

-XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

参数含义 :

-XX:+UseSerialGC 是指使用 Serial + SerialOld 回收器组合
-XX:+PrintGCDetails -verbose:gc 是指打印 GC 详细信息
-Xms20M -Xmx20M -Xmn10M 是指分配给 JVM 的最小,最大以及新生代内存
-XX:SurvivorRatio=8 是指『伊甸园』与『幸存区 From』和『幸存区 To』比例为 8:1:1

  1. 最开始,没什么对象:
public static void main(String[] args) {}

测试结果:
在这里插入图片描述

  1. 当代码改为
    public static void main(String[] args) {byte[] obj1 = new byte[_7MB];}

可以预料到,因为「eden space 8192K, 30% used」已经放不下 7MB 的对象,必然会触发新生代的 GC:
在这里插入图片描述

  1. 可以看到,结果是一部分旧的对象进入了幸存区「from space 1024K, 73% used」,而伊甸园里放入了 7MB 的对象「eden space 8192K, 88% used」再放入一个 512KB 的对象
public static void main(String[] args) {byte[] obj1 = new byte[_7MB];byte[] obj2 = new byte[_512KB];
}

在这里插入图片描述
可以看到,伊甸园几乎被放满了「eden space 8192K, 98% used」,但毕竟没有满,所以没有触发第二次 GC继续放入一个 512KB 的对象.

  1. 果然触发了第二次 GC,其中一个 512KB 的对象进入了幸存区「from space 1024K, 52% used」而那个 7MB 的对象晋升至了老年代「tenured generation total 10240K, used 7848K」
public static void main(String[] args) {byte[] obj1 = new byte[_7MB];byte[] obj2 = new byte[_512KB];byte[] obj3 = new byte[_512KB];
}

在这里插入图片描述

3.多次存活的对象

在幸存区历经多次 GC 还存活的对象会晋升至老年代,默认晋升的阈值是 15,也就是说只要经历 15 次回收不死,肯定晋升,但注意如果目标 survivor 空间紧张,也不必等足 15 次,可以提前晋升

-XX:MaxTenuringThreshold=thresholdSets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector, and 6 for the CMS collector.-XX:TargetSurvivorRatio=percentSets the desired percentage of survivor space (0 to 100) used after young garbage collection. By default, this option is set to 50%.

4.老年代连续空间不足,触发 Full GC

public static void main(String[] args) {byte[] obj1 = new byte[_8MB];byte[] obj2 = new byte[_8MB];
}

第一个 8MB 直接进入老年代,第二个 8MB 对象在分配时发现老年代空间不足,只好尝试先进行一次 Minor GC ,结果发现新生代没有连续空间,只好触发一次 Full GC ,最后发现老年代也没有连续空间,这时出现 OutOfMemoryError

果把代码改为下面的样子,则只会触发 Minor GC ,之后,老年代能够容纳 obj2,所以不会触发 Full GC

public static void main(String[] args) {byte[] obj1 = new byte[_8MB];obj1 = null;;byte[] obj2 = new byte[_8MB];
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++程序设计:对数据文件的操作与文件流

姚老师小课堂开课啦! 一、文件的分类: 1.ASCII码文件: ASCII文件使用方便,比较直观,便于阅读,便于对字符进行输入输出,但一般占用存储空间较多,而且需要花费转换时间(二…

④-2单细胞学习-cellchat单数据代码补充版(通讯网络)

目录 通讯网络系统分析 ①社会网络分析 1,计算每个细胞群的网络中心性指标 2,识别细胞的信号流模式 ②非负矩阵分解(NMF)识别细胞的通讯模式 1,信号输出细胞的模式识别 2,信号输入细胞的模式识别 信…

激光点云配准算法——Cofinet / GeoTransforme / MAC

激光点云配准算法——Cofinet / GeoTransformer / MAC GeoTransformer MAC是当前最SOTA的点云匹配算法,在之前我用总结过视觉特征匹配的相关算法 视觉SLAM总结——SuperPoint / SuperGlue 本篇博客对Cofinet、GeoTransformer、MAC三篇论文进行简单总结 1. Cofine…

计算机网络 期末复习(谢希仁版本)第8章

元文件就是一种非常小的文件,它描述或指明其他文件的一些重要信息。这里的元文件保存了有关这个音频/视频文件的信息。 10. 流式:TCP;流式实况:UDP。

10秒钟docker 安装Acunetix

1、拉取镜像: 2、查看镜像: [rootdns-server ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE quay.io/hiepnv/acunetix latest f8415551b8f4 2 months ago 1.98GB 3、运行镜像: …

Redis 单线程问题 BigKey问题

前言 简单的redis基础类型以及常用操作我们都也已经介绍过了 现在今天我们来谈谈redis对于单线程是需要怎么理解的 以及redis假设遇见大key我们需要怎么去查询和删除呢??? redis单线程 假设有个人现在问你一个问题:redis是单线程的还是多线程的 这个问题本身就不严谨 就像问…

Python通过数据验证功能在Excel文件中创建下拉列表

Excel表格的灵活性和功能性深受各行各业人士的喜爱。在Excel表格中,下拉列表功能是提升数据录入效率与准确性的一个重要利器,能够为用户提供预设的选择项,限制输入范围,避免手动输入错误,还能够简化数据录入过程&#…

深度学习(三)

5.Functional API 搭建神经网络模型 5.1利用Functional API编写宽深神经网络模型进行手写数字识别 import numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom…

Nginx配置详细解释:(3)http模块及server模块,location模块

目录 环境概述: http模块中的全局模块 1. root配置主要是对主web页面的路径访问。 2.server虚拟主机 2.1基于IP: 2.2基于域名: 3.alias别名 4.location匹配 5.access模块: 6.验证模块 7.自定义错误页面 8.日志存放位置…

Clearedge3d EdgeWise 5.8 强大的自动化建模软件

EdgeWise是功能强大的建模软件,提供领先的建模功能和先进的技术,让您的整个过程更快更准确!您可以获得使用自动特征提取和对象识别的 3D 建模,ClearEdge3D 自动建模和对象识别软件通过创建竣工文档和施工验证完成该过程。拓普康和…

Python第二语言(七、Python模块)

目录 1. 什么是模块 2. 基本语法 2.1 模块的导入方式 2.2 基本语法 import 模块名 2.3 基本语法 from 模块名 import 功能名 2.4 基本语法as 别名 3. 自定义模块 4. 调用自定义模块时,如何让其模块中的函数不被调用(__name__) 5. 调…

【数据结构与算法】使用数组实现栈:原理、步骤与应用

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《数据结构与算法》 期待您的关注 ​ 目录 一、引言 🎄栈(Stack)是什么? &#x1…

安卓逆向经典案例——XX牛

安卓逆向经典案例——XX牛 按钮绑定方式 1.抓包 2.查看界面元素,找到控件id 通过抓包,发现点击登录后,才会出现Encrpt加密信息,所以我们通过控件找到对应id:btn_login 按钮绑定方法——第四种 public class LoginA…

开源规则引擎LiteFlow项目应用实践

本文介绍基于开源规则引擎LiteFlow,如何开发规则设计器,在低代码平台中集成规则引擎,并在项目中实现应用的效果。由于低代码平台使用规则引擎实现了逻辑编排的需求,所以本文中的叫法为“逻辑设计”、“逻辑编排”、“逻辑流引擎”…

【Android面试八股文】一图展示 Android生命周期:从Activity到Fragment,以及完整的Android Fragment生命周期

图片来源于:https://github.com/xxv/android-lifecycle Android生命周期:从Activity到Fragment 图:android-lifecycle-activity-to-fragments.png 完整的Android Fragment生命周期 图:complete_android_fragment_lifecycle.png…

设置路径别名

一、描述 如果想要给路径设置为别名,就是常见的有些项目前面的引入文件通过开头的,也就是替换了一些固定的文件路径,怎么配置。 二、配置 import { defineConfig } from vite import react from vitejs/plugin-react import path from path…

基础数据结构 -- 堆

1. 简介 堆可以看做是一种特殊的完全二叉树,它满足任意节点的值都大于或小于其子节点的值。 2. 功能 插入元素:插入新元素时,先将元素放至数组末尾,然后通过上浮算法自底向上调整,使堆保持性质。删除堆顶元素&#xff…

App UI 风格,尽显魅力

精妙无比的App UI 风格

动态内存管理(malloc,calloc,realloc,free)+经典笔试题

动态内存管理 一. malloc 和 free1. malloc2. free 二. calloc三. realloc四.动态内存的错误1.对NULL指针的解引用操作2.对动态开辟空间的越界访问3.对非动态开辟内存使用free释放4.使用free释放一块动态开辟内存的一部分5.对同一块动态内存多次释放6.动态开辟内存忘记释放&…

ROS 获取激光雷达数据(C++实现)

ROS 获取激光雷达数据(C实现) 实现思路 在机器人ROS系统中,激光雷达通常会有一个对应的节点,这个节点一般是由雷达的厂商提供,我们只需要简单的配置以下端口参数,就能和激光雷达的电路系统建立连接&#…