PHP 垃圾回收高级特性

news2025/6/1 23:39:59

PHP 垃圾回收高级特性

1. 循环引用与内存泄漏

单纯的引用计数在遇到循环引用时会导致内存泄漏,主要原因是引用计数无法正确识别那些仅通过循环引用相互关联但实际上已经不可达的对象。

1.1 引用计数的基本原理

引用计数是一种内存管理机制,通过维护每个对象的引用计数来决定对象是否可以被销毁:

  • 创建对象:引用计数初始为 1
  • 新增引用:引用计数增加
  • 删除引用:引用计数减少
  • 销毁对象:当引用计数变为 0 时,对象被销毁,其内存被回收

2. 循环引用详解

2.1 循环引用的定义

循环引用是指两个或多个对象相互引用,形成一个闭环。例如:

$a = new UsersEntity();
$b = new UsersEntity();
$a->ref = $b; // $a 引用了 $b
$b->ref = $a; // $b 引用了 $a

在这个例子中:

  • $a 的引用计数为 1(被 $b->ref 引用)
  • $b 的引用计数为 1(被 $a->ref 引用)

注意:即使脚本中不再使用 $a 和 $b,它们的引用计数都不会变为 0,因为它们相互引用。

2.2 引用计数的局限性

引用计数无法判断循环引用对象是否真正被程序所需。即使这些对象在逻辑上不可达(没有外部引用指向它们),它们之间的引用关系仍然会导致引用计数始终大于 0。


3. 内存泄漏示例

3.1 基本示例

gc_enable(); // 启用垃圾回收
$a = new UsersEntity();
$b = new UsersEntity();
$a->ref = $b; // 循环引用
$b->ref = $a;

unset($a);
unset($b);

重要:即使没有手动触发垃圾回收,这里也会出现内存泄漏。即使 $a 和 $b 已经被 unset,它们仍然在相互引用,引用计数器无法减少到 0。

3.2 实际应用示例

public function getStatusWithCycle()
{
    gc_enable(); // 启用垃圾回收
    $a = new UsersEntity();
    $b = new UsersEntity();
    $a->ref = $b; // 循环引用
    $b->ref = $a;

    unset($a);
    unset($b);
    $endStatus = gc_status();
    return ['end_status' => $endStatus];
}

返回结果示例:

{
    "data": {
        "end_status": {
            "runs": 0,
            "collected": 0,
            "threshold": 10001,
            "roots": 2433
        }
    }
}

4. 垃圾回收器的解决方案

为了弥补引用计数的局限性,PHP 引入了垃圾回收器(GC),采用了基于根集合(roots)和可达性分析的算法:

  1. 根集合:程序中所有可以直接访问的对象
  2. 标记阶段:遍历根集合,标记所有可达的对象
  3. 清除阶段:回收未被标记的对象,包括循环引用的对象
gc_collect_cycles(); // 手动触发垃圾回收

5. 自动垃圾回收机制

如果启用了垃圾回收机制,即使没有手动调用 gc_collect_cycles(),理论上内存溢出的风险大大降低,但仍然可能发生,取决于以下因素:

5.1 自动垃圾回收触发条件

  • PHP 的垃圾回收器在运行时会自动检测是否需要回收循环引用的内存资源
  • 垃圾回收的触发基于根集合的增长(roots)和预定义的阈值(gc_status()['threshold']
  • 如果 roots 增长未达到 threshold,垃圾回收不会触发

注意:如果代码中循环引用对象的生成速度超过垃圾回收器的触发速度,可能出现短期内的内存占用高峰甚至溢出。

5.2 脚本运行时长和负载

短生命周期脚本
  • 大多数 PHP 网页脚本属于此类
  • 脚本结束时会清理所有内存,包括循环引用的对象
  • 通常不会内存溢出,但可能出现瞬间内存使用过高
长生命周期脚本
  • 守护进程、队列处理器、WebSocket 服务等
  • 可能持续运行并产生大量循环引用对象
  • 如果垃圾回收未及时触发,内存使用会逐渐增加

6. 内存管理最佳实践

6.1 如何降低内存溢出风险

  1. 配置优化

    • 确保 memory_limit 配置足够高
    • 适当调整垃圾回收阈值
  2. 代码优化

    • 避免频繁创建循环引用对象
    • 及时打破不必要的引用关系
  3. 主动管理

    • 使用 unset() 及时打破引用关系
    • 在适当位置手动触发垃圾回收

6.2 监控和优化建议

  1. 内存监控

    • 定期检查 gc_status() 的 roots 和 collected 值
    • 引入内存监控和日志机制
  2. 性能优化

    • 优化代码结构
    • 减少不必要的对象创建
    • 适当调用 gc_collect_cycles()

总结

关键要点

  • 循环引用是引用计数机制的主要缺陷
  • PHP 的垃圾回收器通过可达性分析解决循环引用问题
  • 合理使用手动垃圾回收和内存监控可有效预防内存溢出
  • 在高负载场景下需要特别注意内存管理

通过优化代码结构和适当调用 gc_collect_cycles(),可以有效避免内存溢出问题。在实际应用中,应结合具体场景选择合适的内存管理策略。

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

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

相关文章

如何用Python抓取Google Scholar

文章目录 [TOC](文章目录) 前言一、为什么要抓取Google Scholar?二、Google Scholar 抓取需要什么三、为什么代理对于稳定的抓取是必要的四、一步一步谷歌学者抓取教程4.1. 分页和循环4.2. 运行脚本 五、完整的Google Scholar抓取代码六、抓取Google Scholar的高级提…

Wireshark对usb设备进行抓包找不到USBPcap接口的解决方案

引言 近日工作需要针对usb设备进行抓包,但按照wireshark安装程序流程一步步走,即使勾选了安装USBPcap安装完成后开启wireshark依然不显示USBPcap接口,随设法进行解决。 最终能够正常显示USBPcap接口并能够正常使用进行抓包 解决方案&#x…

Socket 编程 UDP

目录 1. UDP网络编程 1.1 echo server 1.1.1 接口 1.1.1.1 创建套接字 1.1.1.2 绑定 1.1.1.3 bzero 1.1.1.4 htons(主机序列转网络序列) 1.1.1.5 inet_addr(主机序列IP转网络序列IP) 1.1.1.6 recvfrom(让服务…

Jenkins实践(8):服务器A通过SSH调用服务器B执行Python自动化脚本

Jenkins实践(8):服务器A通过SSH调用服务器B执行Python自动化脚本 1、需求: 1、Jenkins服务器在74上,Python脚本在196服务器上 2、需要在服务器74的Jenkins上调用196上的脚本执行Python自动化测试 2、操作步骤 第一步:Linux Centos7配置SSH免密登录 Linux Centos7配置S…

lua的注意事项2

总之,下面的返回值不是10,a,b 而且

前端八股之HTML

前端秘籍-HTML篇 1. src和href的区别 src 用于替换当前元素,href 用于在当前文档和引用资源之间确立联系。 (1)src src 是 source 的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置&#xff1…

鲲鹏Arm+麒麟V10,国产化信创 K8s 离线部署保姆级教程

Rainbond V6 国产化部署教程,针对鲲鹏 CPU 麒麟 V10 的离线环境,手把手教你从环境准备到应用上线,所有依赖包提前打包好,步骤写成傻瓜式操作指南。别说技术团队了,照着文档一步步来,让你领导来都能独立完成…

【C++ Qt】认识Qt、Qt 项目搭建流程(图文并茂、通俗易懂)

每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论​: 本章将开启Qt的学习,Qt是一个较为古老但仍然在GUI图形化界面设计中有着举足轻重的地位,因为它适合嵌入式和多种平台而被广泛使用…

IoT/HCIP实验-1/物联网开发平台实验Part2(HCIP-IoT实验手册版)

文章目录 概述产品和设备实例的产品和设备产品和设备的关联单个产品有多个设备为产品创建多个设备产品模型和物模型设备影子(远程代理) 新建产品模型定义编解码插件开发编解码插件工作原理消息类型与二进制码流添加消息(数据上报消息&#xf…

Replacing iptables with eBPF in Kubernetes with Cilium

source: https://archive.fosdem.org/2020/schedule/event/replacing_iptables_with_ebpf/attachments/slides/3622/export/events/attachments/replacing_iptables_with_ebpf/slides/3622/Cilium_FOSDEM_2020.pdf 使用Cilium,结合eBPF、Envoy、Istio和Hubble等技术…

数学建模之最短路径问题

1 问题的提出 这个是我们的所要写的题目,我们要用LINGO编程进行编写这个题目,那么就是需要进行思考这个怎么进行构建这个问题的模型 首先起点,中间点,终点我们要对这个进行设计 2 三个点的设计 起点的设计 起点就是我们进去&am…

测试概念 和 bug

一 敏捷模型 在面对在开发项目时会遇到客户变更需求以及合并新的需求带来的高成本和时间 出现的敏捷模型 敏捷宣言 个人与交互重于过程与工具 强调有效的沟通 可用的软件重于完备的文档 强调轻文档重产出 客户协作重于合同谈判 主动及时了解当下的要求 相应变化…

zynq 级联多个ssd方案设计(ECAM BUG修改)

本文讲解采用zynq7045芯片如何实现200T容量高速存储方案设计,对于大容量高速存储卡,首先会想到采用pcie switch级联方式,因为单张ssd的容量是有限制的(目前常见的m.2接口容量为4TB,U.2接口容量为16TB)&…

brep2seq 论文笔记

Brep2Seq: a dataset and hierarchical deep learning network for reconstruction and generation of computer-aided design models | Journal of Computational Design and Engineering | Oxford Academic 这段文本描述了一个多头自注意力机制(MultiHead Attenti…

【运维实战】Linux 中设置 sudo ,8个有用的 sudoers 配置!

在Linux及其他类Unix操作系统中,只有 root 用户能够执行所有命令并进行关键系统操作,例如安装更新软件包、删除程序、创建用户与用户组、修改重要系统配置文件等。 但担任 root 角色的系统管理员可通过配置sudo命令,允许普通系统用户执行特定…

江科大SPI串行外设接口hal库实现

hal库相关函数 初始化结构体 typedef struct {uint32_t Mode; /*SPI模式*/uint32_t Direction; /*SPI方向*/uint32_t DataSize; /*数据大小*/uint32_t CLKPolarity; /*时钟默认极性控制CPOL*/uint32_t CLKPhase; /*…

[网页五子棋][对战模块]前后端交互接口(建立连接、连接响应、落子请求/响应),客户端开发(实现棋盘/棋子绘制)

文章目录 约定前后端交互接口建立连接建立连接响应针对"落子"的请求和响应 客户端开发实现棋盘/棋子绘制部分逻辑解释 约定前后端交互接口 对战模块和匹配模块使用的是两套逻辑,使用不同的 websocket 的路径进行处理,做到更好的耦合 建立连接 …

【ArcGIS Pro微课1000例】0071:将无人机照片生成航线、轨迹点、坐标高程、方位角

文章目录 一、照片预览二、生成轨迹点三、照片信息四、查看方位角五、轨迹点连成线一、照片预览 数据位于配套实验数据包中的0071.rar,解压之后如下: 二、生成轨迹点 地理标记照片转点 (数据管理),用于根据存储在地理标记照片文件(.jpg 或 .tif)元数据中的 x、y 和 z 坐…

Ubuntu Zabbix 钉钉报警

文章目录 概要Zabbix警监控脚本技术细节配置zabbix告警 概要 提示:本教程用于Ubuntu ,zabbix7.0 Zabbix警监控脚本 提示:需要创建一个脚本 #检查是否有 python3 和版本 rootzabbix:~# python3 --version Python 3.12.3在/usr/lib/zabbix/…

threejs顶点UV坐标、纹理贴图

1. 创建纹理贴图 通过纹理贴图加载器TextureLoader的load()方法加载一张图片可以返回一个纹理对象Texture,纹理对象Texture可以作为模型材质颜色贴图.map属性的值。 const geometry new THREE.PlaneGeometry(200, 100); //纹理贴图加载器TextureLoader const te…