Jvm知识点二(GC)

news2024/9/21 15:46:29

GC 相关知识点

    • 一、垃圾收集器
    • 二、 java 中的引用
    • 三、 怎么判断对象是否可以被回收?
    • 四、 Java对象在虚拟机中的生命周期
    • 五、垃圾收集算法
      • 标记-清除算法
      • 复制算法
        • 补充知识点
          • 深拷贝和浅拷贝
      • 标记-压缩算法(Mark-Compact)
      • 分代收集算法
      • Java堆的分区
    • 六、内存分配策略

一、垃圾收集器

垃圾收集器(Garbage Collection),通常被称作GC。提到GC,很多人认为它是伴随Java而出现的,其实GC出现的时间要比Java早太多了,它是1960年诞生于MIT的Lisp。GC主要做了两个工作,一个是内存的划分和分配,另一个是对垃圾进行回收
关于内存的划分和分配,目前Java虚拟机内存的划分是依赖于GC设计的,比如现在GC都是采用了分代收集算法来回收垃圾的,Java堆作为GC主要管理的区域,被细分为新生代和老年代,再细致一点新生代又可以划分为Eden空间、FromSurvivor空间、To Survivor空间等,这样划分是为了更快地进行内存分配和回收。空间划分后,GC就可以为新对象分配内存空间。关于对垃圾进行回收,被引用的对象是存活的对象,而不被引用的对象是死亡的对象(也就是垃圾),GC要区分出存活的对象和死亡的对象(也就是垃圾标记),并对垃圾进行回收。在对垃圾进行回收前,GC要先标记出垃圾,那么如何标记呢?目前有两种垃圾标记算法,分别是引用计数算法和根搜索算法,这两个算法都和引用有些关联,因此讲垃圾标记算法前,我们先回顾一下引用的知识点。

二、 java 中的引用

在JDK1.2之后,Java将引用分为强引用软引用弱引用虚引用

  1. 强引用: 当我们新建一个对象时就创建了一个具有强引用的对象,如果一个对象具有强引用,垃圾收集器就绝不会回收它。Java虚拟机宁愿抛出OutOfMemoryError异常,使程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题。
  2. 软引用: 如果一个对象只具有软应用,当内存不够时,会回收这些对象的内存,回收后如果还是没有足够的内存,就会抛出OutOfMemoryError异常。Java 提供了SoftReference 类来实现软引用。
  3. 弱引用: 弱引用比起软引用具有更短的生命周期,垃圾收集器一旦发现了只具有弱应用的对象,不管当前内存是否足够,都会收回它的内存。Java 提供了WeakReference 类来实现弱引用。
  4. **虚引用:**虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,这就和没有任何应用一样,在任何时候都可能被垃圾收集器回收。一个只具有虚引用的对象,被垃圾收集器回收时会收到一个系统通知,这也是虚引用的主要作用。Java 提供了PhantomReference 类来实现虚引用。

三、 怎么判断对象是否可以被回收?

垃圾收集器在做垃圾回收的时候,首先需要判断的就是哪些内存是需要被回收的,哪些对象是「存活」的,是不可以被回收的;哪些对象已经「死掉」了,需要被回收。
一般有两种方法来判断:

  1. 引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器加1,引用被释放时计数减1.当计数器为0时,则该对象就不能被使用,变成了垃圾。
    举个例子,在下面代码的注释1和注释2处,d1和d2相互引用,除此之外这两个对象无任何其他引用,实际上这两个对象已经死亡,应该作为垃圾被回收,但是由于这两个对象互相引用,引用计数就不会为0,如果Java虚拟机采用了引用计数算法,垃圾收集器就无法回收它们。
    在这里插入图片描述

  2. 根搜索算法(可达性分析算法):这个算法的基本思想就是选定一些对象作为GC Roots ,并组成跟对象集合,然后以这些GC Roots 的对象作为起始点,向下搜索,如果目标对象到GC Roots 是连着的,我们则称该目标对象是可达的(搜索所走过的路径称为引用链),如果目标对象不可达则说明对象是可以被回收的对象,如下图
    在这里插入图片描述
    可以看出,Obj5、Obj6和Obj7都是不可达的对象,其中Obj5和Obj6虽然互相引用,但是因为它们到GC Roots是不可达的,所以它们仍旧被判定为可回收的对象,这样根搜索算法就解决了引用计数算法无法解决的问题:已经死亡的对象因为相互引用而不能被回收。在Java中,可以作为GC Roots的对象主要有以下几种:

  3. Java 栈中引用的对象

  4. 本地方法栈中JNI 引用的对象

  5. 方法区中运行时常量池引用的对象

  6. 方法区中静态属性引用的对象

  7. 运行中的线程

  8. 由引导类加载器加载的对象

  9. GC 控制的对象

问题 :被标记为不可达的对象会立即被垃圾收集器回收吗?

要回答这个问题我们首先要了解Java对象在虚拟机中的生命周期,如下

四、 Java对象在虚拟机中的生命周期

在Java 对象被类加载器加载到虚拟机中后,Java 对象在Java 虚拟机中有7个阶段

  1. 创建阶段(Created)
    创建阶段的具体步骤为:
    (1)为对象分配存储空间
    (2)构造对象
    (3)从超类到子类对static 成员进行初始化
    (4)递归调用超累的构造方法
    (5)调用子类的构造方法

  2. 应用阶段(In Use)
    当对象被创建,并分配给变量赋值时,状态就切换到了应用阶段。这一阶段的对象至少要具有一个强引用,或者显示的使用软引用、弱引用或者虚引用。

  3. 不可见阶段(Invisible)
    在程序中找不到对象的任何强引用,比如程序的执行已经超出了该对象的作用域。在不可见阶段,对象仍可能被特殊的强引用GC Roots持有着,比如对象被本地方法栈中JNI 应用或被运行中的线程应用等

  4. 不可达阶段(Unreachable)
    在程序中找不到对象的任何强引用,并且垃圾收集器发现对象不可达。

  5. 收集阶段(Collected)
    垃圾收集器已经发现对象不可达,并且垃圾收集器已经准备好要对该对象的内存空间重新进行分配,这个时候如果该对象重写了finalize 方法,则会调用该方法(finalize在java 9y以后被废弃。应调用时机不确定,不建议重写该方法)

  6. 终结阶段(Finalized)
    在对象执行完finalize 方法后仍然处于不可达状态时,或者对象没有重写finalize 方法,则该对象进入终结阶段,并等待垃圾收集器回收该对象空间

  7. 对象空间重新分配阶段(Deallocated)
    当垃圾收集器对对象的内存空间进行回收或者在分配时,这个对象就会彻底消失

很显然是不会的,被标记为不可达的对象会进入收集阶段,这时会执行该对象重写的finalize方法,如果没有重写finalize方法或者finalize方法中没有重新与一个可达的对象进行关联才会进入终结阶段,并最终被回收

五、垃圾收集算法

标记-清除算法

标记—清除算法(Mark-Sweep)是一种常见的基础垃圾收集算法,它将垃圾收集分为两个阶段。

  • 标记阶段:标记出可以回收的对象
  • 清除阶段:回收被标记的对象所占用的空间

标记-清除算法之所以是基础的,是因为后面讲到的垃圾收集算法都是在此算法的基础上进行改进的

优点:实现简单,不需要对象进行移动
缺点:标记和清除过程效率低,产生大量不连续的内存碎片,碎片太多可能会导致后续没有足够的连续内存分配给较大的对象,从而提前触发新的一次垃圾收集动作,提高了垃圾回收的频率。
在这里插入图片描述

复制算法

为了解决标记-清除算法的效率不高的问题,产生了复制算法。它把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾收集时,遍历当前使用的区域,把存活对象复制到另外一个区域中,最后将当前使用的区域的可回收的对象进行回收。

优点:按顺序分配内存即可,这种算法每次都对整个半区进行内存回收,不需要考虑内存碎片的问题。实现简单、运行高效。
缺点:可用的内存大小缩小为原来的一半。复制算法的效率与存活对象的数目多少有很大的关系,如果存活对象很少,复制算法的效率就会很高。由于绝大多数对象的生命周期很短,并且这些生命周期很短的对象都存于新生代中,所以复制算法被广泛用于新生代中。
在这里插入图片描述

补充知识点

深拷贝和浅拷贝
  • 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址
  • 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误

区别
浅拷贝:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变
深复制:在计算机中开辟一块新的内存地址用于存放复制的对象

标记-压缩算法(Mark-Compact)

在新生代中可以使用复制算法,但是在老年代就不能选择复制算法了,因为老年代的对象存活率会较高,这样会有较多的复制操作,导致效率变低。标记—清除算法可以应用在老年代中,但是它效率不高,在内存回收后容易产生大量内存碎片。因此就出现了一种标记—压缩(Mark-Compact)算法,与标记—清除算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使它们紧凑地排列在一起,然后对边界以外的内存进行回收,回收后,已用和未用的内存都各自一边。
在这里插入图片描述
优点:解决了标记-清理算法存在的内存碎片问题。
缺点:仍需要进行局部对象移动,一定程度上降低了效率。

分代收集算法

分代收集算法会结合不同的收集算法来处理不同的空间,因此在学习分代收集算法之前我们首先需要了解Java堆区的空间划分。Java 堆区的划分在Java 虚拟机中,各种对象的生命周期会有着较大的差别,大部分对象的生命周期很短暂,少部分对象的生命周期很长。有的甚至与应用程序以及Java虚拟机的运行周期一样长。因此,应该对不同生命周期的对象采取不同的收集策略,根据生命周期长短将他们分别放到不同的区域,并在不同的区域采用不同的手机算法,这就是分代的概念。
在这里插入图片描述

Java堆的分区

Java 堆区基于分代的概念,分为新生代(Young Generation)老年代(Tenured Generation)
新生代再细分为 :

  • Eden空间
  • From Survivor空间
  • To Survivor 空间

因为Eden空间中的大多数对象生命周期很短,所以新生代的空间划分并不是均分的,HotSpot虚拟机默认Eden 空间和两个 Survior 空间的比例是 8:1:1。

在接收分代收集的执行流程前,我们先明确几个分代收集的概念

  • Minor GC: 是指发生在新生代的GC ,因为Java对象大多都是朝生夕死,所以Minor GC 非常频繁,一般回收速度也非常快
  • Major GC:是指发生在老年代的GC ,出现了Major GC 通常都会伴随至少一次Minor GC。 Major GC 的速度通常会比Minor GC 慢上10倍以上
  • Full GC:是清理整个堆空间—包括年轻代和老年代

当Eden区满的时候,JVM会触发MinorGC,如下图
在这里插入图片描述
当发生一次Minor GC的时候,它的执行流程如下:

  1. 把Eden 空间的存活对象复制到To Survivor空间。并把经过一次Minor GC 并在Form Survivor 空间存活的任然年轻的对象也复制到 ToSurvivor 空间
  2. 清空Eden 空间和From Survivor 空间
  3. From Survivor 和 To Survivor 空间交换,From Survivor 变 To
    Survivor,To Survivor 变 From Survivor

每次在From Survivor 到To Survivor 空间移动时都存活的对象,年龄加1,当年龄到达所指定的阈值(默认是15)时,升级为老年代。大对象也会直接进入老年代。如下图
在这里插入图片描述

六、内存分配策略

所谓自动内存管理,最终要解决的也就是内存分配和内存回收两个问题。前面我们介绍了内存回收,这里我们再来聊聊内存分配。对象的内存分配通常是在 Java 堆上分配(随着虚拟机优化技术的诞生,某些场景下也会在栈上分配,后面会详细介绍),对象主要分配在新生代的 Eden 区,如果启动了本地线程缓冲,将按照线程优先在 TLAB 上分配。少数情况下也会直接在老年代上分配。总的来说分配规则不是百分百固定的,其细节取决于哪一种垃圾收集器组合以及虚拟机相关参数有关,但是虚拟机对于内存的分配还是会遵循以下几种「普世」规则:

  • 对象优先在Eden区分配:多数情况,对象都在新生代 Eden 区分配。当 Eden 区分配没有足够的空间进行分配时,虚拟机将会发起一次 Minor GC。如果本次 GC 后还是没有足够的空间,则将启用分配担保机制在老年代中分配内存
  • 大对象直接进入老年代:所谓大对象是指需要大量连续内存空间的对象,频繁出现大对象是致命的,会导致在内存还有不少空间的情况下提前触发 GC 以获取足够的连续空间来安置新对象。前面我们介绍过新生代使用的是标记-清除算法来处理垃圾回收的,如果大对象直接在新生代分配就会导致 Eden 区和两个 Survivor 区之间发生大量的内存复制。因此对于大对象都会直接在老年代进行分配。
  • 长期存活对象将进入老年代:虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须判断哪些对象应该放在新生代,哪些对象应该放在老年代。因此虚拟机给每个对象定义了一个对象年龄的计数器,如果对象在 Eden 区出生,并且能够被Survivor 容纳,将被移动到 Survivor 空间中,这时设置对象年龄为 1。对象在 Survivor 区中每「熬过」一次 Minor GC 年龄就加 1,当年龄达到一定程度(默认 15) 就会被晋升到老年代。

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

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

相关文章

SSH实验部署

一,实验要求 1,两台机器:第一台机器作为客户端,第二台机器作为服务器,在第一台使用rhce用户免 密登录第二台机器 2,禁止root用户远程登录和设置三个用户sshuser1, sshuser2, sshuser3, 只允许ss…

三维数学(二)

欧拉角 使用物体在三个旋转轴上的旋转角度来保存方位 API: Transform.eulerAngles:返回或设置物体的欧拉角 优点: 1.仅使用三个数字表达方位,占用空间小 2.沿坐标轴旋转的单位为角度,符合人的思考方式 3.任意…

OSPF网络类型实验配置(华为)

OSPF网络类型实验配置(华为): 根据实验要求,我们可以把其拆分成为两个部分来做,分别做两个部分的MGRE: 通过拆分可以更加直观的看到路由器之间的信息传输,然后分别做R1,R2,R3和R1,R4,R5的MGRE:…

【Xilinx】如何自动格式化Verilog代码

开发环境VivadoVSCode 【Xilinx】自动格式化Verilog代码前言一、安装VSCode并修改Vivado的默认编辑器二、安装Verilog插件1. 语法插件2. 格式化插件三、演示:如何代码格式化1. 插件演示2. 修改默认插件附录前言 有时候接手别人的代码,或者从网上找的开源…

2023学习心得01

2023年,加足马力,继续提升自己! 这次来分享下最近的学习心得,以便自己后续回顾可快速上手 按键框架数字,文字取模菜单框架Main总体框架1.首先来分析按键的框架,这里用到了函数指针,不同的可以…

并查集(C++)

根据下面这道题讲下并查集 (其实本来是写题解的…写着写着就变成算法说明了) [蓝桥杯 2017 国 C] 合根植物(C,并查集) 题目描述 w 星球的一个种植园,被分成 mnm \times nmn 个小格子(东西方…

【深度腐蚀】深入聊聊KMP算法

思路分析:主串str遍历主串j子串sub遍历子串iKMP算法是一种字符串匹配算法,他通过Next 数组能使i不回退,这样大大减少了无效的比对,提高了字符串匹配的速度。Next数组:要想让i不回退,就需要让j回退到合适的位…

HTTPS】HTTPS过程详解,tcpdump抓包 全过程分析

RFC中的HTTPS交互过程如下: 抓包分析 Client Hello 客户端支持的TLS最高版本号 客户端生成的随机数 客户端支持的加密套件 主机名server_name cipher suite怎么理解 名字为 ECDH-ECDSA-AES128-SHA256 的CipherSuite 使用 ECDH做密钥交换, 使用ECDS…

21. 反爬工程师都会用的手段,IP限制反爬 - 爬虫训练场

本篇博客我们实现的案例是 IP 限制反爬,翻译过来就是每个 IP 在规定时间内限制访问次数。 例如,可以限制单 IP 每秒访问 5 次,超过之后就会返回 403 错误。 Flask 实现 IP 限制使用 Flask 插件自定义中间件限制 IP自定义请求钩子使用 Flask 插…

CSS3 滤镜效果

文章目录CSS3 滤镜效果概述说明使用案例鬼屋效果代码下载CSS3 滤镜效果 概述 在CSS3中,新增了滤镜效果,可以轻松实现黑白效果、复古效果、亮度效果等。 说明 语法 filter: 取值;filter属性取值 属性值说明brightness(百分比)亮度brightness()方法的…

一款数据可视化分析报表工具

在这个数据信息化时代,每分每秒都产生海量数据,在海量数据中,挖掘出有用的数据,并且能以直观的方式展示这些数据,变得尤为重要,大家或许还在为做报表感到为难,想在众多数据中处理,查…

leetcode-hot链表专题——206. 反转链表

206. 反转链表 递归法 ListNode* reverse(ListNode *pre,ListNode *cur){if(cur NULL) return pre;ListNode *next cur->next;cur->next pre;return reverse(cur,next);}ListNode* reverseList(ListNode* head) {return reverse(NULL,head);}迭代法 ListNode* rever…

Python爬虫编写乱码问题、验证码登录问题和IP代理问题解决

今天继续给大家介绍Python爬虫相关知识,本文主要内容是Python爬虫编写乱码问题、验证码登录问题和IP代理问题解决。 一、乱码问题解决 我们在使用Python爬虫爬取网页信息时,有时会遇上乱码问题(特别是爬取中文网页信息时)&#…

C语言进阶——指针(二)

一. 函数指针 说到指针,我们可以想到的是取地址操作符 int ADD(int a,int b) {return ab; } int main() {printf("%p\n", &ADD);return 0; } 如此,我们便可以得到一个地址 而我们便可以将这个地址存入到一个函数指针中 int(*p)(int,…

各种卷积的说明

一、普通卷积 1、多通道输入,单通道输出 输入为三通道的6*6*3,过滤器也是三通道的分别对应RGB三个通道。其中: 过滤器的通道数需要和被卷积目标的通道数保持一致。输出通道数卷积核个数计算过程包含了先卷积再融合的过程。3个通道各种卷积得…

图像处理:二值掩膜影像去噪与边缘强化

前言这篇博客主要解决的一个问题是掩膜图像的噪声去除和边缘强化,如下图1所示。可以看到掩膜图像上有很多的斑点噪声,而且掩膜的轮廓也不够清晰。所以我们的目标就是一方面尽可能把这些斑点噪声去除,另一方面尽量突出掩膜边界。另外处理后的掩…

c#入门-多播委托,匿名函数

多播委托 委托作为变量,也可以和-。委托可以和方法组相加,但方法组和方法组不能相加。 储存多个函数时,调用委托会按照加的顺序依次执行。但返回值只使用最后绑定的函数。 使用 – 时,如果储存了这个值,那么会移除第一…

深圳电子行业的mes系统的需求分析方法~先达智控

深圳电子行业mes系统的需求分析方法导读:如今,制造业的行业竞争越来越激烈,减少产品成本、提高产品质量,缩短开发周期,已成为当前企业生产与发展中的一个重点方向。而对电子行业而言,厂家着重考虑并解决了产…

我们想要赚钱,就要理解赚钱的本质、要素、公式和障碍

想要赚钱,就要先理解赚钱,只有理解了赚钱,才能够赚到钱。赚钱的本质是商业,而商业的本质是交易,那交易的本质又是什么呢?就是价值交换。价值交换,就是我们怎么能与别人达成合作,并且…

朱江明赋予了零跑新的活力

作为今年风云的智能自主化新能源汽车品牌,零跑汽,一度是人们谈论的热点话题。零跑汽车在今年也取得了非常不错的销量,这么一个响当当的品牌在之前其实发展几度遭遇挫折,而正是创始人朱江明几次将零跑汽车起死回生,赋予…