搜索
首页后端开发php教程php的扩展和嵌入--php内存管理_PHP教程

php对内存的管理机制相当的详尽,它在这一点上更类似与java的垃圾回收机制。而对于c语言或者c++大部分时候都只能由程序员自己把申请的空间释放掉。在php中,由于要应对成千上万的连接,同时这些连接往往还需要保持很长的时间。这并不同于c中程序结束了相应的内存块就会被回收。

所以仅仅依靠程序员在写程序的时候注意内存回收是不够的,php肯定要有一些自己内部的、与连接相关的内存管理机制来保证不发生任何的内存泄露。

在本文中,首先对php的内存机制进行一个介绍:

那些在c语言中的空间函数,比如malloc() free() strdup() realloc() calloc(),php中会有不同的形式。


返还申请的内存:对于程序员来说,每一块申请的内存都应该返还,如果不还就会导致内存泄漏。在那些不要求一直运行的程序中,稍许的内存泄漏在整个进程被杀掉之后就结束了。但是类似于apache这种一直运行的web server,小的内存泄漏最终会导致程序的崩溃。


错误处理的例子

在进行错误处理的时候,采用的机制一般是是Zend Engine会设定一个跳出地址,一旦发生exit或die或任何严重错误E_ERROR的时候,就会利用一个longjmp()跳到这个地址上面去。但是这种做法几乎都会导致内存泄漏。因为free的操作都会被跳掉。(这个问题在c++里面也同样存在,就是在设计类的时候,绝不要把错误处理或告警函数写在构造或者析构函数内,同样的原因,由于对象已经处在了销毁或创建的阶段,所以任何错误函数处理都可能打断这一过程,从而可能导致内存泄漏。) 下面的代码中就给出了这样的一个例子:
void call_function(const char *fname, int fname_len TSRMLS_DC)
{
    zend_function *fe;
    char *lcase_fname;
    /* PHP function names are case-insensitive to simplify locating them in the function tables all function names are implicitly
     * translated to lowercase
     */
    lcase_fname = estrndup(fname, fname_len);//创造一个函数名的副本
    zend_str_tolower(lcase_fname, fname_len);//都转换成小写,这样的寻找的时候很方便,这应该也是php函数表中进行函数标识的方式。
    if (zend_hash_find(EG(function_table),
            lcase_fname, fname_len + 1, (void **)&fe) == FAILURE) {?SUCCESS。这个是要在函数表里面寻找待调用的函数。
        zend_execute(fe->op_array TSRMLS_CC);
    } else {
              php_error_docref(NULL TSRMLS_CC, E_ERROR,
                         "Call to undefined function: %s()", fname); //等同于Trigger_error() 
    }
    efree(lcase_fname);
}
在这个例子中,提供了一个php在调用函数时候的功能。当php调用函数时,需要到函数表也就是function_table中去寻找相应的函数,而在寻找之前要先转换到小写字母,这样在寻找的时候可以提高查找的效率。 而通过zend_hash_find函数如果找到了要调用的函数,就使用zend_execute进行调用。而如果没找到的haunted就要跳出报错,显示没找到。但是问题来了,注意之前为了寻找函数创建了一个小写版本的函数名字符串。这个字符串一直到用到zend_hash_find函数,一旦没找到进入了报错之后,那么这个字符串所对应的内存空间必然就找不回来了,这就造成了内存的泄露。

因此,php提供了Zend内存管理,Zend memory management也称为ZendMM。 \
  • php中的内存管理与操作系统的机制类似,但是对象是针对每一个请求所涉及的内存的。
  • 除此之外ZendMM还会控制ini文件里面规定的memZ喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcnlfbGltaXSjrNKyvs3Kx8u10ru1qcO/uPbH68fzy/nSqsfztcTE2rTms6y5/cHL1eK49m1lbW9yeSBsaW1pdKOsxMfDtNKyu+HJ6sfryqew3KGjPGxpPtTazbzW0LXE1+7PwsPmv7S1vcHLy/zT67LZ1/fPtc2zz+DBqs+1tcTSu7LjoaPV67bUstnX98+1zbPW0LXEserXvLXExNq05snqx+u6zcrNt8W1xLe9t6ijrHBocNbQtrzT0LbU06a1xLqvyv2ho9Xi0Km6r8r9sqKyu8rH0ru49rzytaW1xMzmu7ujrMv8w8fW0LD8uqzT0MzYtqi1xNDFz6KjrNTa1eLQqdDFz6K1xLDv1vrPwr7NxNy5u7DRw7+49sfrx/PL+cnqx+u1xMTatOa/6b340NCx6sq2oaPV4tH5vs3E3Lm7yrXP1rbUw7+49sfrx/O1xMTatObH+NPyvfjQ0LfWsfC1xLncwO2hozxsaT7NrMqx1NrNvNbQv7S1vcHL0ru5ssG91tbE2rTmx+vH87XEt73KvaO6cGVyc2lzdGVudLrNcGVyLXJlcXVlc3SjrLbU09pwZXJzaXN0ZW50wLTLtbLusru24Lj6z7XNs7XEx+vH877N0rvR+cHLo6zSsr7NysfLtcrHtsDBotTaw7/Su7j2x+vH89auzeK1xKOssru74dTax+vH873hyvjWrrrzsbu72MrVoaO1q8rH09DKsbryyse38XBlcnNpc3RlbnS/ycTc0qpydW50aW1lssXE3NaqtcCjrMv50tTU2tXi1tbH6b/2z8KjrNDo0qrSu7j2ZmxhZ8C01rjKvtXi0ru146GjttTT2srHt/HKx3BlcnNpc3RlbnSjrL340NDE2rTmx+vH87XEt73KvcrHsrvSu9H5tcSho8/Cw+a4+LP2ttTTprnYz7WjugoKPHVsPgo8bGk+cGVtYWxsb2MoYnVmZmVyX2xlbiwxKSA9PSBtYWxsb2MoYnVmZmVyX2xlbik8bGk+cGVybWFsbG9jKGJ1ZmZlcl9sZW4sMCkgPT0gZW1hbGxvYyhidWZmZXJfbGVuKdXi1tbBqs+1ysfTw7rqtqjS5bXEt73Kvb72tqi1xDxsaT4jZGVmaW5lIHBlbWFsbG9jKHNpemUscGVyc2lzdGVudCkgXDxsaT4KCgo8bGk+Cjx1bD4KKChwZXJzaXN0ZW50KT9tYWxsb2Moc2l6ZSk6ZW1hbGxvYyhzaXplKSkKCmZsYWc9MbHtyr7Kx3BlcnNpc3RlbnS1xKOszqowse3KvrK7ysejrL7NuPrSu7DjtcS4vcr009rH68fztcRlbWFsbG9j0rvR+cHLoaMKPGJyPgoKCjxicj4KCs/CzbzW0L/J0tS/tLW9z7XNs7XExNq05snqx+u6r8r90+twaHDW0LXExNq05snqx+u6r8r9tcS21LHI16q7u828o7oKCjxpbWcgc3JjPQ=="http://www.Bkjia.com/uploadfile/Collfiles/20131213/20131213091641239.jpg" alt="\">
    如果你对malloc、calloc和realloc这些函数还不太熟悉,请移步: http://www.cppblog.com/sandywin/archive/2011/09/14/155746.html
    除此之外,还有两个安全模式的内存函数: void *safe_emalloc(size_t size, size_t count, size_t addtl);
    void *safe_pemalloc(size_t size, size_t count, size_t addtl, char persistent); 他们申请的空间是size*count + addtl,存在的原因是为了避免int型的溢出。


    接下来说一个更有趣的,php中的引用计数
    很多语言中都有引用,很多时候也都会使用引用。通过引用可以节省空间,因为有时候并没有必要为每个变量都制造一个副本。 所谓引用计数,就是指同一块内存空间被多少个变量引用了,从而避免可能的内存错误操作。 先看下面的一段代码:
    	* {
    	*     zval *helloval;
    	*     MAKE_STD_ZVAL(helloval);
    	*     ZVAL_STRING(helloval, "Hello World", 1);
    	*     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	*     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	* }
    
    这段代码首先声明了一个zval变量,再用MAKE_STD_ZVAL进行了初始化,接下来用ZVAL_STRING附了初值。然后对这个变量,给出了两个变量名。第一个是a,第二个是b,毫无疑问,第二个肯定是一个引用。但是这段代码这么写肯定有问题,问题就在于你在用zend_hash_add之后并没有更新相应的引用计数。zend并不知道你多加了这么一个引用,这就导致释放内存的时候可能导致两次释放。所以经过修改之后的正确代码如下:
    	* {
    	*     zval *helloval;
    	*     MAKE_STD_ZVAL(helloval);
    	*     ZVAL_STRING(helloval, "Hello World", 1);
    	*     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	*     <strong>ZVAL_ADDREF(helloval);</strong>//加上这个之后,就不会有重新释放同一块内存空间这样的错误了
    	*     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	* }
    
    进行了ZVAL_ADDREF之后,下一次unset变量的时候,会先查看ref_count引用计数,如果=1就释放,如果>1就只是-1,并不进行内存释放。
    Copy on Write 再来看下面的这一段php代码:
    <?php
        $a = 1;
        $b = $a;
        $b += 5;
    ?>
    很显然在第二行的时候b声明了一个a的引用,那么在执行完了第三行的代码之后,b增加了,a增不增加呢?很多时候可能并不想增加。所以这个时候当Zend检测到refCount>1之后,就会执行一个变量分离的操作,把原来的一块内存变成两块内存:
    zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC)
    {
        zval **varval, *varcopy;
        if (zend_hash_find(EG(active_symbol_table),
                           varname, varname_len + 1, (void**)&varval) == FAILURE) {
           /*符号表里没找到 */
           return NULL;
       }
       if ((*varval)->refcount < 2) {
           /* varname 是唯一的引用,什么也不用做 */
           return *varval;
       }
       /* 否则的话,不是唯一的引用,给zval*做一个副本 */
           MAKE_STD_ZVAL(varcopy);
           varcopy = *varval;
       /* Duplicate any allocated structures within the zval* */
           zval_copy_ctor(varcopy); //这一块是怎么拷贝的?mark 应该已经跟varval对应的varname连起来了
       /* 把varname的版本删掉,这会减少varval的引用次数 */
           zend_hash_del(EG(active_symbol_table), varname, varname_len + 1);
       /* 初始化新创造的值的引用次数,然后附给varname变量 */
           varcopy->refcount = 1;
           varcopy->is_ref = 0;
           zend_hash_add(EG(active_symbol_table), varname, varname_len + 1,
                                            &varcopy, sizeof(zval*), NULL);
       /* Return the new zval* */
       return varcopy;
    }
    首先看到了两个判断语句,第一个判断语句先在符号表里面看看有没有找到相应的变量,如果没找到也就没必要分离了。第二个判断语句是看输入的变量的引用次数是不是小于2,如果是的话那就说明输入变量*varval是唯一的,也没必要分离。 否则的话肯定有引用,这个时候就要制作一个副本varcopy。这个副本会承袭varname对应的值,但是不同之处在于帮它重新申请了内存空间,重新初始化了refcount和is_ref参数。 以a、b为例,在$b+=5,执行之后,b作为varname去寻找是否有引用,发现还有一个引用a,这个时候就把b的值拷出来,然后重新申请一片空间,在重新注册为b。这样的话就是两块独立的内存块了。

    Change on Write 再看一个代码片段:
    <?php
        $a = 1;//执行完这一句之后,a变量的ref_count是1,is_ref是0
        $b = &$a;//这一句之后,变量(zval*)的ref_count是2,然后由于显示的&,is_ref为1
        $b += 5;// 这个时候在执行这一句的时候就不会有任何的分离
    ?>
    如果你觉得想要a跟着b一起改变,那没有问题,只要显式的用&符号进行引用声明就可以了。这样的话is_ref标志位就会被置1. 这时候也就没必要进行内存块的分离了。所以在上面的代码中要把第二个if语句的判断更改一下:
    if ((*varval)->is_ref || (*varval)->refcount < 2) {
        /* varname is the only actual reference,
         * or it's a full reference to other variables
         * either way: no separating to be done
         */
        return *varval;
    }


    再看最后一种情况,这种情况最纠结:
    <?php
        $a = 1;
        $b = $a;
        $c = &$a;
    ?>
    既不是copy on write也不是change on wirte,那没办法了,只好分离一下。这里只好b独立出来了:







    对php内存管理的一些机制就说到这里,感觉php确实是一门相当神奇的语言。哈哈。

    www.bkjia.comtruehttp://www.bkjia.com/PHPjc/621625.htmlTechArticlephp对内存的管理机制相当的详尽,它在这一点上更类与java的垃圾回收机制。而对于c语言或者c大部分时候都只能由程序员自己把申请的空间...
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
PHP的当前状态:查看网络开发趋势PHP的当前状态:查看网络开发趋势Apr 13, 2025 am 12:20 AM

PHP在现代Web开发中仍然重要,尤其在内容管理和电子商务平台。1)PHP拥有丰富的生态系统和强大框架支持,如Laravel和Symfony。2)性能优化可通过OPcache和Nginx实现。3)PHP8.0引入JIT编译器,提升性能。4)云原生应用通过Docker和Kubernetes部署,提高灵活性和可扩展性。

PHP与其他语言:比较PHP与其他语言:比较Apr 13, 2025 am 12:19 AM

PHP适合web开发,特别是在快速开发和处理动态内容方面表现出色,但不擅长数据科学和企业级应用。与Python相比,PHP在web开发中更具优势,但在数据科学领域不如Python;与Java相比,PHP在企业级应用中表现较差,但在web开发中更灵活;与JavaScript相比,PHP在后端开发中更简洁,但在前端开发中不如JavaScript。

PHP与Python:核心功能PHP与Python:核心功能Apr 13, 2025 am 12:16 AM

PHP和Python各有优势,适合不同场景。1.PHP适用于web开发,提供内置web服务器和丰富函数库。2.Python适合数据科学和机器学习,语法简洁且有强大标准库。选择时应根据项目需求决定。

PHP:网络开发的关键语言PHP:网络开发的关键语言Apr 13, 2025 am 12:08 AM

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP:许多网站的基础PHP:许多网站的基础Apr 13, 2025 am 12:07 AM

PHP成为许多网站首选技术栈的原因包括其易用性、强大社区支持和广泛应用。1)易于学习和使用,适合初学者。2)拥有庞大的开发者社区,资源丰富。3)广泛应用于WordPress、Drupal等平台。4)与Web服务器紧密集成,简化开发部署。

超越炒作:评估当今PHP的角色超越炒作:评估当今PHP的角色Apr 12, 2025 am 12:17 AM

PHP在现代编程中仍然是一个强大且广泛使用的工具,尤其在web开发领域。1)PHP易用且与数据库集成无缝,是许多开发者的首选。2)它支持动态内容生成和面向对象编程,适合快速创建和维护网站。3)PHP的性能可以通过缓存和优化数据库查询来提升,其广泛的社区和丰富生态系统使其在当今技术栈中仍具重要地位。

PHP中的弱参考是什么?什么时候有用?PHP中的弱参考是什么?什么时候有用?Apr 12, 2025 am 12:13 AM

在PHP中,弱引用是通过WeakReference类实现的,不会阻止垃圾回收器回收对象。弱引用适用于缓存系统和事件监听器等场景,需注意其不能保证对象存活,且垃圾回收可能延迟。

解释PHP中的__ Invoke Magic方法。解释PHP中的__ Invoke Magic方法。Apr 12, 2025 am 12:07 AM

\_\_invoke方法允许对象像函数一样被调用。1.定义\_\_invoke方法使对象可被调用。2.使用$obj(...)语法时,PHP会执行\_\_invoke方法。3.适用于日志记录和计算器等场景,提高代码灵活性和可读性。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具