Java 和 PHP GC 的差异和差异出现的原因
JAVA 的 GC 处理
判断草死掉的两种方式:引用计数和可达性分析
可达性分析对 JAVA 比较好用的原因是 JAVA遵守这面向对象的严格要求,每个变量都被对象包裹,所以每个变量都能通过对象来进行遍历找到,最终判断他们的是否被引用;
但对于 PHP 这类面向过程下构建的面向对象,找变量引用就比较零碎了,所以主要是引用计数为主;
便捷记忆,在一片空地上,死掉的草都清理(标记清除),就会出现零碎的光秃点,随着秃点增多,将不是秃点的草都挪到新的空地(复制),这样腾出的地和秃点合在一起形成新的空地,有些草活得比较久,有些草活得短,根据查看的频次将活得短的聚合在一起,活得长的聚合在一起,就是分代算法。
Java的垃圾回收(Garbage Collection,GC)机制是一种自动管理内存的技术,它负责检测和清理不再被引用的对象,以释放内存并防止内存泄漏。在Java中,开发人员无需手动释放内存,因为这是由垃圾回收器自动完成的。Java的垃圾回收机制主要基于以下两个原则:
-
引用计数(Reference Counting):这是一种简单的垃圾回收方法,它通过跟踪每个对象的引用计数来确定对象是否可以被回收。当引用计数为零时,对象被认为是不再被引用的,可以被回收。然而,这种方法容易出现循环引用问题,因此在Java中并没有广泛使用。
-
可达性分析(Reachability Analysis):这是Java主要使用的垃圾回收方法。它从根对象(如主方法中的局部变量和静态变量)开始,通过引用链追踪对象是否可以被访问。如果一个对象不再通过任何引用链与根对象相连,那么这个对象就被认为是不可达的,可以被回收。
以下是Java中几种主要的垃圾回收算法:
-
标记-清除算法(Mark and Sweep):
- 这是最基本的垃圾回收算法。
- 首先,它通过可达性分析标记所有可达对象。
- 然后,它扫描整个堆,清理未标记的对象。
- 这个算法的问题是会产生内存碎片,可能会导致堆内存不连续,进而影响程序性能。
-
复制算法(Copying):
- 这个算法将堆内存分为两个区域:一半是已使用的区域,一半是空的。
- 它首先在已使用区域进行标记,然后将存活的对象复制到空区域,最后清理已使用区域。
- 这个算法的优点是避免了内存碎片问题,但需要额外的内存用于复制对象。
-
标记-整理算法(Mark and Compact):
- 这个算法结合了标记-清除和复制算法的优点。
- 首先,它标记可达对象,然后将它们压缩到堆的一端,清理掉堆的另一端的垃圾对象。
- 这个算法也可以减少内存碎片,但不需要额外的内存。
-
分代算法(Generational):
- 这个算法根据对象的年龄将堆分成不同的代。
- 年轻代中的对象通常具有较短的生命周期,老年代中的对象具有较长的生命周期。
- 垃圾回收频繁地发生在年轻代,而较少发生在老年代。
- 这个算法利用了对象的特性,提高了回收效率。
PHP 的 GC 处理
PHP 使用引用计数(Reference Counting)作为主要的垃圾回收机制。每个变量和对象在 PHP 中都有一个引用计数器,用于跟踪对象的引用关系。当一个变量或对象的引用计数降为零时,它就会被认为是不再被引用的,可以被垃圾回收。
以下是 PHP 引用计数垃圾回收机制的基本工作原理:
-
引用计数:每当一个变量或对象引用另一个变量或对象时,引用计数会增加1。当一个变量不再引用某个对象时,引用计数会减少1。这个计数器是实时更新的,反映了对象的当前引用情况。
-
回收不可达对象:垃圾回收器会定期检查引用计数,并找出那些引用计数为零的对象。这些对象被认为是不再被引用的,可以安全地回收内存。
-
循环引用问题:引用计数垃圾回收机制对于循环引用问题并不十分有效。循环引用是指一组对象互相引用,导致它们的引用计数永远不会降为零。为了解决这个问题,PHP 引入了周期性垃圾回收(Cycle Collector),它定期检测并处理循环引用。
-
周期性垃圾回收:周期性垃圾回收器负责检测循环引用,并释放被这些循环引用困住的内存。它会在特定的时间间隔内运行,识别并处理循环引用,从而防止内存泄漏。
Python 的 GC 处理
Python 的垃圾回收(Garbage Collection,GC)机制主要基于引用计数和循环垃圾回收两个核心原理,以自动管理内存。以下是 Python 的垃圾回收机制的详细说明:
-
引用计数(Reference Counting):
- Python 中的每个对象都有一个引用计数器,用于跟踪对象的引用关系。
- 当一个对象被引用时,其引用计数会增加1,当一个对象不再被引用时,其引用计数会减少1。
- 当引用计数降为零时,说明对象不再被任何变量或数据结构引用,可以被垃圾回收。
-
循环垃圾回收:
- 引用计数机制可以有效地处理大多数情况,但对于循环引用(两个或多个对象相互引用形成环)的情况无法处理。
- 为了解决循环引用问题,Python 引入了循环垃圾回收器(Cycle Collector)。
- 循环垃圾回收器定期检查引用关系图,找出并清理不可达的循环引用。
-
分代垃圾回收:
- Python 的垃圾回收还引入了分代垃圾回收策略,将对象分为三代:年轻代、中年代和老年代。
- 大部分新创建的对象放入年轻代,如果在几轮垃圾回收后仍然存活,它们会晋升到中年代,然后到老年代。
- 基于分代的策略利用了对象的生命周期模式,提高了垃圾回收的效率。
-
垃圾回收周期:
- Python 的垃圾回收不是持续运行的,而是在特定条件下触发。
- 垃圾回收器会在一组触发条件满足时运行,如年轻代中对象的数量超过阈值、内存分配请求失败等。
- 当触发条件满足时,垃圾回收器会进行一轮或多轮垃圾回收,清理不可达对象。
-
gc
模块:- Python 提供了一个名为
gc
的模块,用于手动控制和配置垃圾回收机制。 - 开发人员可以使用
gc
模块中的函数来手动触发垃圾回收、禁用垃圾回收或配置垃圾回收参数。
- Python 提供了一个名为
PHP GC 和 Python GC 的差异
PHP 和 Python 是两种不同的脚本语言,它们的垃圾回收(GC)算法和策略存在一些差异。以下是 PHP 和 Python 的 GC 算法之间的主要区别:
PHP 的 GC 算法:
-
引用计数:PHP 主要使用引用计数来管理内存。每个变量和对象都有一个引用计数,当引用计数变为零时,变量或对象就被回收。这个方法简单且实时,但容易出现循环引用问题。
-
周期性垃圾回收:为了解决循环引用问题,PHP 引入了周期性垃圾回收(Cycle Collector)。周期性垃圾回收器会定期检查对象之间的引用关系,以便释放不再使用的内存。这是 PHP 针对循环引用问题的一种补救措施。
-
Zend Memory Manager:PHP 使用 Zend Memory Manager 来管理内存。它负责内存分配和释放,以及跟踪引用计数和垃圾回收。Zend Memory Manager 可以通过配置参数来调整垃圾回收行为。
Python 的 GC 算法:
-
引用计数和循环垃圾回收:Python 同样使用引用计数来追踪对象的引用关系。但与 PHP 不同,Python 还实现了循环垃圾回收器,用于检测并处理循环引用。
-
分代垃圾回收:Python 的垃圾回收还引入了分代垃圾回收策略。Python 将对象分为三代:年轻代、中年代和老年代。新创建的对象会放入年轻代,如果经过一定数量的垃圾回收循环后仍然存活,它们会被提升到中年代,以此类推。这个策略利用了观察到的对象生命周期模式,提高了垃圾回收的效率。
-
垃圾回收周期:Python 的垃圾回收器不是定期运行的,而是基于阈值和触发条件来触发的。具体触发条件包括年轻代对象数量、年轻代空间占用率等。这个策略可以降低垃圾回收对性能的影响。
GO 语言的 GC
Go语言(或简称Golang)的垃圾回收(GC)是一种自动管理内存的机制,旨在减轻开发人员手动管理内存的负担,提高程序的可靠性和性能。Go语言的GC机制具有以下特点和原则:
-
并发垃圾回收:Go语言的GC是并发的,这意味着垃圾回收器可以在程序运行的同时执行。它不会导致应用程序停顿(Stop-The-World),因此对于高并发和实时性要求高的应用程序非常适用。
-
分代垃圾回收:Go语言的GC使用分代垃圾回收策略,将堆内存分为三代:新生代、中年代和老年代。新分配的对象通常放入新生代,存活时间较长的对象会升级到中年代和老年代。这种分代策略提高了垃圾回收的效率。
-
标记-清除算法:Go语言的垃圾回收器使用标记-清除(Mark-and-Sweep)算法。它首先标记所有活动对象,然后清除未标记的对象。这个算法能够处理循环引用并有效地回收不再使用的内存。
-
三色标记法:Go语言的GC引入了三色标记法,将对象标记为白色、黑色和灰色。黑色表示已经标记为活动对象,白色表示未标记的对象,灰色表示待处理的对象。这个方法有助于减少标记和清除的阶段。
-
垃圾回收时间控制:Go语言的垃圾回收器可以通过设置环境变量来控制垃圾回收的时间和频率。这使得开发人员可以根据应用程序的需求进行调整,以平衡性能和内存使用。
-
内存分配优化:Go语言的运行时系统还包括了一个内存分配器,它针对小对象和大对象进行了优化,以减少内存碎片和提高分配性能。
都有哪些场景会用到循环引用?
循环引用是指两个或多个对象之间相互引用,形成一个环形的引用关系。这种情况通常不是有意为之,而是一种错误或者设计不当的情况。尽管循环引用通常是不推荐的,但有一些特定的情景可能需要它们,尤其是在某些编程模型或设计中:
-
数据结构中的循环引用:在某些数据结构中,循环引用是有意为之的。例如,双向链表中的每个节点都包含对前一个和后一个节点的引用,因此它们会形成一个环形引用。这种数据结构在某些情况下非常有用。
-
图数据结构:图是由节点和边组成的数据结构,其中节点之间可能存在循环引用。图数据结构通常用于表示网络、关系和复杂的数据关联。在这种情况下,循环引用是必要的。
-
观察者模式:在观察者模式中,主题对象(Subject)和观察者对象(Observer)之间可能存在循环引用。主题对象需要通知观察者对象进行更新,而观察者对象通常也需要引用主题对象以便取消注册。这种情况下,循环引用是合理的。
-
资源管理:某些资源需要引用它们的持有者。例如,一个对象可能需要引用一个打开的文件、数据库连接或网络连接。这种情况下,资源可能会被多个对象持有,形成循环引用,以确保在没有对象引用资源时不会被关闭。
Javascript 循环引用的一个案例
class Subject {constructor() {this.observers = [];}addObserver(observer) {this.observers.push(observer);}notify(message) {this.observers.forEach(observer => {observer.update(message);});}
}class Observer {constructor(name) {this.name = name;}update(message) {console.log(`${this.name} received message: ${message}`);}
}const subject = new Subject();
const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");subject.addObserver(observer1);
subject.addObserver(observer2);observer1.subject = subject; // 主题引用了观察者
observer2.subject = subject;subject.notify("Hello, observers!");
在观察者模式中,观察者通常引用主题对象是为了实现以下目的:
-
访问主题数据:观察者需要访问主题对象的数据或状态以执行相应的操作。通过引用主题对象,观察者可以轻松地获取主题的信息。
-
响应主题的变化:观察者的主要任务是响应主题对象的状态变化。通过引用主题对象,观察者可以注册自己,并接收主题的通知,从而知道何时以及如何更新自己。
-
与主题建立松耦合关系:引用主题对象有助于观察者与主题对象之间建立松耦合的关系。观察者不需要了解主题对象的内部实现细节,只需关注主题的状态变化即可
相关文章:
Java 和 PHP GC 的差异和差异出现的原因
JAVA 的 GC 处理 判断草死掉的两种方式:引用计数和可达性分析 可达性分析对 JAVA 比较好用的原因是 JAVA遵守这面向对象的严格要求,每个变量都被对象包裹,所以每个变量都能通过对象来进行遍历找到,最终判断他们的是否被引用&…...
loguru logger使用
一、基本使用 ①标准使用 from loguru import logger# 在标准输出里面输出一行debug日志 logger.debug("Thats dubug")②设置输出格式 from loguru import loggerlogger.remove(0) # 先删除格式 logger.add(sink./logger.log, format"{time: %Y-%m-%d %H:%M…...
vue-自适应布局-postcss-pxtorem
原理: 比如一个375px设计稿 其中一个320px宽度的元素 如何实现自适应布局呢? 其实可以这样理解: 我们先计算出375屏幕时候320px的大小,在屏幕变化时候,这些元素都会等比例缩放 比如屏幕从375 变为750px时候࿰…...

9.12|day 5|day 44 |完全背包| 518. 零钱兑换 II | 377. 组合总和 Ⅳ
● 完全背包 主要是看清01背包和完全背包的区别 //01背包 for(int i 0;i<weight.size();i){ for(int j bagWeight;j>weight[i];j--){dp[j] Math.max(dp[j],dp[j-weight[i]]value[i]); } } //完全背包 for(int i 0;i<weight.size();i){for(int j weight[i];j<…...
C++ 中的原子变量(std::atomic)使用指南
目录 C 中的原子变量(std::atomic)使用指南基本概念使用方法创建原子变量读取值修改值原子操作 常见应用场景1. 计数器2. 控制标志3. 链表和数据结构 示例代码结论 C 中的原子变量(std::atomic)使用指南 原子变量(std…...

【用unity实现100个游戏之9】使用Unity制作类八方旅人、饥荒风格的俯视角2.5D游戏
前言 2.5D游戏 是一种介于二维和三维之间的游戏形式。它通常在二维平面上展示游戏内容,但利用三维技术来实现更加逼真的图像效果。 在2.5D游戏中,角色和环境通常是以平面的形式呈现,但可以在垂直方向上移动。这意味着玩家可以在一个相对较薄…...

如何在群晖中,正确配置 docker 的 ipv6 地址
参考 2023年9月12日 https://synocommunity.com/ https://github.com/wangliangliang2/fix_synology_docker_ipv6 https://post.smzdm.com/p/an3np8m7/ 正文 关于这个话题,国内搜索引擎得到的结果出奇的一致,且过时。 (看的我脑壳痛&#…...

XSS入门 XSS Challenges
level1(直接注入) <script>alert(xss)</script>level2(双引号闭合标签) 测试 <sCr<ScRiPt>IPT>OonN"\/(hrHRefEF)</sCr</ScRiPt>IPT>发现<>"被转换,构造新的语句 "><script>alert(/xss/)</…...

李沐《动手学深度学习》torch.cat() 和 torch.stack()的区别及思考
一、问题引出 好久没更新啦!最近在学习沐神《动手学深度学习》6.5节池化层的时候,发现沐神在两处相似的地方使用了两种Python拼接函数torch.cat()和torch.stack(): 百思不得其解,于是查阅相关文档之后终于弄清楚了两者之间的区别…...

【算法与数据结构】235、LeetCode二叉搜索树的最近公共祖先
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:本题和这道题类似【算法与数据结构】236、LeetCode二叉树的最近公共祖先,相同的算法也能解…...

bboss 流批一体化框架 与 数据采集 ETL
数据采集 ETL 与 流批一体化框架 特性: 高效、稳定、快速、安全 bboss 是一个基于开源协议 Apache License 发布的开源项目,主要由以下三部分构成: Elasticsearch Highlevel Java Restclient , 一个高性能高兼容性的Elasticsea…...

JVM详细教程
JVM 前言 还在完善中先发布 JVM虚拟机厂家多钟多样,具体实现细节可能不一样,这里主要讲的是虚拟机的规范,以下内容融合了各个平台发布的内容和周志明老师的《深入理解java虚拟机》 JVM概述 如何理解jvm跨平台? 编译成汇编代码…...

Smartbi吴华夫:后疫情时代,BI发展趋势的观察与应对
沿着旧地图找不到新大陆,“基于指标体系的可视化分析和增强分析”成为BI发展新阶段。Smartbi V11系列新品与时俱进,以指标为核心,同时融合BI应用,赋能管理者和业务,成为引领数字化运营的新航标! ——思迈特…...

软件设计模式系列之三———工厂方法模式
1 模式的定义 工厂方法模式是一种常见的设计模式,属于创建型设计模式之一,它在软件工程中用于对象的创建。该模式的主要思想是将对象的创建过程抽象化,将具体对象的实例化延迟到子类中完成,以便在不同情况下可以创建不同类型的对…...
pytorch 多卡分布式训练 调用all_gather_object 出现阻塞等待死锁的问题
pytorch 多卡分布式训练 torch._C._distributed_c10d中的函数all_gather_object 出现阻塞等待死锁的问题 解决办法就是 在进程通信之前调用torch.cuda.set_device(local_rank) For NCCL-based processed groups, internal tensor representations of objects must be moved …...

SpringMvc增删改查
SpringMvc增删改查 一、前期准备二、逆向生成增删改查2.2.aspect切面层2.3.Mybatis generator逆向生成2.4.根据生成代码编写Biz层与实现类 三、controller层代码编写四、前台代码与分页代码五、案例测试 一、前期准备 1.2.导入pom.xml依赖 <?xml version"1.0" …...
【计算机网络】网络编程接口 Socket API 解读(5)
Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。 本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。…...

手动实现一个bind函数!
原文地址:手动实现一个bind函数! - 知乎 1.bind函数用法 bind()方法用于创建一个新的函数,这个新函数接收的第一个参数代表的就是this,利用bind()函数我就就可以任意改变函数内部的this指向了。 官网的解释: bind()…...

数据结构-时间复杂度/空间复杂度
Hello,好久没有更新了哦,已经开始学习数据结构了,这篇文章呢就是对刚学数据结构所接触到的时间复杂度进行一个分享哦,如果有错误之处,大家记得拍拍我哦~ 既然要讨论时间/空间复杂度,那我们就得知道时间/空…...
英语写作中“展示”、“表明”demonstrate、show、indicate、illustrate的用法
一、demonstrate、show、indicate在论文写作中主要用法是:demonstrate/show/indicate 从句: Sb./Sth. demonstrates/shows/indicates that ……从句中一般表达事实、观点和结论等。 例句: The authors demonstrated/showed/indicated that…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...