Jadual Kandungan
zval 的结构
循环引用造成的内存泄漏
object和array的回收过程
垃圾回收的原理
例子
Rumah pembangunan bahagian belakang PHP7 php7如何实现垃圾回收机制

php7如何实现垃圾回收机制

May 24, 2021 am 09:21 AM
php7 Mekanisme pengumpulan sampah

本篇文章给大家介绍一下php7实现垃圾回收机制的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

php7如何实现垃圾回收机制

在了解我们 php GC 时,我觉得我有必要介绍一下们的 php 的变量在底层的实现。

zval 的结构

// php 变量对于的c结构体
struct _zval_struct {
    zend_value value;
    union {
       ……
    } u1;
    union {
        ……
    } u2;
};
Salin selepas log masuk

由于主要讲垃圾回收,所以在这里简单介绍下 u1 u2 联合体的功能
u1 结构比较复杂,我认为主要是用于识别变量类型
u2 这里面大多都是辅助字段,变量内部功能的实现、提升缓存友好性等等
接下来是我们的主角

zend_value 它也是结构体中内嵌的一个联合体

typedef union _zend_value {
    zend_long         lval;//整形
    double            dval;//浮点型
    zend_refcounted  *counted;//获取不同类型的gc头部
    zend_string      *str;//string字符串
    zend_array       *arr;//数组
    zend_object      *obj;//对象
    zend_resource    *res;//资源
    zend_reference   *ref;//是否是引用类型
  
    // 忽略下面的结构,与我们讨论无关
    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        ZEND_ENDIAN_LOHI(
            uint32_t w1,
            uint32_t w2)
    } ww;
} zend_value;
Salin selepas log masuk

在 zval的 value中就记录了引用计数zend_refcounted *counted这个类型,我们的垃圾回收机制也是基于此的。

typedef struct _zend_refcounted_h {
    uint32_t         refcount;          /* reference counter 32-bit */
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,    /* used for strings & objects */
                uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
        } v;
        uint32_t type_info;
    } u;
} zend_refcounted_h;
Salin selepas log masuk

所有的复杂类型的定义, 开始的时候都是zend_refcounted_h结构, 这个结构里除了引用计数以外, 还有GC相关的结构. 从而在做GC回收的时候, GC不需要关心具体类型是什么, 所有的它都可以当做zend_refcounted*结构来处理.
#变量的自动回收

在php中 除了 array和object类型的变量,其余大部分是自动回收
php 普通变量的回收和 该变量的引用次数有关。

官方的例子

$a = 1;
$b = $a;
xdebug_debug_zval('a');
$a =10;
xdebug_debug_zval('a');
unset($a);
xdebug_debug_zval('a');
Salin selepas log masuk

结果

a:
(refcount=2, is_ref=0),int 1
a:
(refcount=1, is_ref=0),int 10
a: no such symbol
Salin selepas log masuk

可以看到 当$a =10 的时候 涉及到 php的COW(copy-on-write)机制,$b 会复制一份原先的 $a ,解除了他们之间的引用关系,所以a的引用次数(refcount)减少为1。

然后我们uset($a)之后 a的引用次数变为0。这就会被认为是垃圾变量,释放空间。

再举一个例子

$a = [1];
$a[1] = &$a;
unset($a);
Salin selepas log masuk

在 unset($a) 之前 $a 的类型为引用类型

a:
(refcount=2, is_ref=1),
array (size=2)
  0 => (refcount=1, is_ref=0),int 1
  1 => (refcount=2, is_ref=1),
    &array<
Salin selepas log masuk

unset($a) 之后,就变成这样

这时候,我们unset操作时refcount 由2变为1,因为有内部引用指向 $a,所以在外部 其所占用的空间并不会被销毁。

然后我们的外部引用已经被中断了,我们也不能使用它。它就成了一个“孤儿”,在c语言中叫做野指针。在php中叫做循环引用。内存泄漏。想要销毁变量的话,只能等 php脚本结束。

循环引用造成的内存泄漏

为了清理这些垃圾,引入了两个准则

  • 如果引用计数减少到零,所在变量容器将被清除(free),不属于垃圾

  • 如果一个zval 的引用计数减少后还大于0,那么它会进入垃圾周期。其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。

循环引用基本上只会出现在 数组和对象中,对象是因为它的本身就是引用

object和array的回收过程

php7的垃圾回收包含两个部分,一个是垃圾收集器,一个是垃圾回收算法。

垃圾收集器,把刚刚提到的,可能是垃圾的元素收集到回收池中 也就是把变量的 zend_refcount>0的变量 放在回收池中。 当回收池的值达到一定额度了,会进行统一遍历处理。进行模拟删除,如果zend_refcount=0那就认为是垃圾,直接删除它。

遍历回收池中的每一个变量,根据每一个变量,再遍历每一个成员,如果成员还有嵌套的话继续遍历。然后把所有成员的 做模拟的 refcount -1。如果此时外部的变量的 引用次数为 0 。那么可以视为垃圾,清楚。如果大于0,那么恢复引用次数,并从垃圾回收池中取出。

垃圾回收的原理

如果你这个变量不是垃圾,那么它的所有成员变量的引用减一之后,必然不会是总变量的引用为0。

例子

说的比较死,不如举个例子。刚刷 sf.gg 的时候看到一道关于 GC 的题,我回答了一波。关于GC垃圾回收机制

题目如下
题目

//我的回答
1、只要zval.value的refcount减一,然后缺其refcount的值不为0那么它就可能是垃圾,进入垃圾周期。
2、进入垃圾池遍历所有成员,包括其嵌套的成员,都对其做 refcount-1的操作,看外部的引用是否为0。

那么对于 题主的问题来说,
首先,你要想$a为垃圾,一定要先对 unset($a)操作,那么此时 $a的 refcount = 2
对于$a[0] refcount-1 不影响外部的$a,
$a[1] refcount-1 ,此时 $a的 refount=1
$a[2] refcount-1 ,此时 $a 的 refount=0 
模拟减结束,那么此变量被当成垃圾回收。
Salin selepas log masuk

推荐学习:php视频教程

Atas ialah kandungan terperinci php7如何实现垃圾回收机制. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Arahan sembang dan cara menggunakannya
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk memasang sambungan mongo dalam php7.0 Bagaimana untuk memasang sambungan mongo dalam php7.0 Nov 21, 2022 am 10:25 AM

Cara memasang sambungan mongo dalam php7.0: 1. Buat kumpulan pengguna dan pengguna mongodb 2. Muat turun pakej kod sumber mongodb dan letakkan pakej kod sumber dalam direktori "/usr/local/src/" 3. Masukkan direktori "src/" 4. Buka zip pakej kod sumber 5. Buat direktori fail mongodb 6. Salin fail ke direktori "mongodb/" 7. Buat fail konfigurasi mongodb;

Bagaimana untuk menyelesaikan masalah apabila php7 mengesan bahawa port tcp tidak berfungsi Bagaimana untuk menyelesaikan masalah apabila php7 mengesan bahawa port tcp tidak berfungsi Mar 22, 2023 am 09:30 AM

Dalam php5, kita boleh menggunakan fungsi fsockopen() untuk mengesan port TCP. Fungsi ini boleh digunakan untuk membuka sambungan rangkaian dan melakukan beberapa komunikasi rangkaian. Tetapi dalam php7, fungsi fsockopen() mungkin menghadapi beberapa masalah, seperti tidak dapat membuka port, tidak dapat menyambung ke pelayan, dsb. Untuk menyelesaikan masalah ini, kita boleh menggunakan fungsi socket_create() dan fungsi socket_connect() untuk mengesan port TCP.

Apakah yang perlu saya lakukan jika pemalam dipasang dalam php7.0 tetapi ia masih menunjukkan bahawa ia tidak dipasang? Apakah yang perlu saya lakukan jika pemalam dipasang dalam php7.0 tetapi ia masih menunjukkan bahawa ia tidak dipasang? Apr 02, 2024 pm 07:39 PM

Untuk menyelesaikan pemalam yang tidak menunjukkan isu yang dipasang dalam PHP 7.0: Semak konfigurasi pemalam dan dayakan pemalam. Mulakan semula PHP untuk menggunakan perubahan konfigurasi. Semak kebenaran fail pemalam untuk memastikan ia betul. Pasang kebergantungan yang hilang untuk memastikan pemalam berfungsi dengan betul. Jika semua langkah lain gagal, bina semula PHP. Punca lain yang mungkin termasuk versi pemalam yang tidak serasi, memuatkan versi yang salah atau isu konfigurasi PHP.

Artikel untuk bercakap tentang mekanisme kutipan sampah dalam php Artikel untuk bercakap tentang mekanisme kutipan sampah dalam php Aug 26, 2022 am 10:48 AM

Artikel ini akan memberi anda pemahaman yang mendalam tentang mekanisme pengumpulan sampah dalam PHP. Saya harap ia akan membantu anda!

Apr 09, 2024 pm 01:33 PM

Penyelesaian biasa untuk persekitaran pelayan PHP termasuk memastikan versi PHP yang betul dipasang dan fail yang berkaitan telah disalin ke direktori modul. Lumpuhkan SELinux buat sementara waktu atau selama-lamanya. Semak dan konfigurasikan PHP.ini untuk memastikan sambungan yang diperlukan telah ditambah dan disediakan dengan betul. Mulakan atau mulakan semula perkhidmatan PHP-FPM. Semak tetapan DNS untuk isu penyelesaian.

Cara memasang dan menggunakan php7.0 Cara memasang dan menggunakan php7.0 Nov 30, 2022 am 09:56 AM

Cara memasang dan menggunakan php7.0: 1. Pergi ke laman web rasmi PHP untuk memuat turun versi pemasangan yang sepadan dengan sistem tempatan 2. Ekstrak fail zip yang dimuat turun ke direktori yang ditentukan 3. Buka tetingkap baris arahan dan pergi ke direktori "E:\php7" Hanya jalankan arahan "php -v".

Menyahsulit pengurusan memori dan mekanisme pengumpulan sampah bahasa Go Menyahsulit pengurusan memori dan mekanisme pengumpulan sampah bahasa Go Nov 30, 2023 am 09:17 AM

Bahasa Go ialah bahasa pengaturcaraan yang cekap, selamat dan serentak Reka bentuk pengurusan memori dan mekanisme pengumpulan sampah juga merupakan ciri uniknya. Artikel ini akan menyahsulit pengurusan memori dan mekanisme pengumpulan sampah bahasa Go secara mendalam. 1. Pengurusan memori Dalam bahasa Go, pengurusan memori merangkumi dua aspek: peruntukan memori dan pelepasan memori. 1.1 Peruntukan memori Dalam bahasa Go, kami memperuntukkan memori melalui fungsi terbina dalam baharu dan buat. Antaranya, baru mengembalikan penunjuk kepada nilai sifar yang baru diperuntukkan, manakala membuat pulangan jenis tertentu dan panjangnya.

Bagaimana cara menetapkan kebenaran secara automatik UnixSocket selepas sistem dimulakan semula? Bagaimana cara menetapkan kebenaran secara automatik UnixSocket selepas sistem dimulakan semula? Mar 31, 2025 pm 11:54 PM

Bagaimana untuk menetapkan keizinan UnixSocket secara automatik selepas sistem dimulakan semula. Setiap kali sistem dimulakan semula, kita perlu melaksanakan perintah berikut untuk mengubahsuai keizinan UnixSocket: sudo ...

See all articles