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提供了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变量,再用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); * zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), * &helloval, sizeof(zval*), NULL); * }
登入後複製进行了ZVAL_ADDREF之后,下一次unset变量的时候,会先查看ref_count引用计数,如果=1就释放,如果>1就只是-1,并不进行内存释放。* { * 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); * }
登入後複製
Copy on Write 再来看下面的这一段php代码:很显然在第二行的时候b声明了一个a的引用,那么在执行完了第三行的代码之后,b增加了,a增不增加呢?很多时候可能并不想增加。所以这个时候当Zend检测到refCount>1之后,就会执行一个变量分离的操作,把原来的一块内存变成两块内存:<?php $a = 1; $b = $a; $b += 5; ?>
登入後複製首先看到了两个判断语句,第一个判断语句先在符号表里面看看有没有找到相应的变量,如果没找到也就没必要分离了。第二个判断语句是看输入的变量的引用次数是不是小于2,如果是的话那就说明输入变量*varval是唯一的,也没必要分离。 否则的话肯定有引用,这个时候就要制作一个副本varcopy。这个副本会承袭varname对应的值,但是不同之处在于帮它重新申请了内存空间,重新初始化了refcount和is_ref参数。 以a、b为例,在$b+=5,执行之后,b作为varname去寻找是否有引用,发现还有一个引用a,这个时候就把b的值拷出来,然后重新申请一片空间,在重新注册为b。这样的话就是两块独立的内存块了。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; }
登入後複製
Change on Write 再看一个代码片段:如果你觉得想要a跟着b一起改变,那没有问题,只要显式的用&符号进行引用声明就可以了。这样的话is_ref标志位就会被置1. 这时候也就没必要进行内存块的分离了。所以在上面的代码中要把第二个if语句的判断更改一下:<?php $a = 1;//执行完这一句之后,a变量的ref_count是1,is_ref是0 $b = &$a;//这一句之后,变量(zval*)的ref_count是2,然后由于显示的&,is_ref为1 $b += 5;// 这个时候在执行这一句的时候就不会有任何的分离 ?>
登入後複製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; }
登入後複製
再看最后一种情况,这种情况最纠结:既不是copy on write也不是change on wirte,那没办法了,只好分离一下。这里只好b独立出来了:<?php $a = 1; $b = $a; $c = &$a; ?>
登入後複製
对php内存管理的一些机制就说到这里,感觉php确实是一门相当神奇的语言。哈哈。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

在PHP8 中,match表達式是一種新的控制結構,用於根據表達式的值返回不同的結果。 1)它類似於switch語句,但返回值而非執行語句塊。 2)match表達式使用嚴格比較(===),提升了安全性。 3)它避免了switch語句中可能的break遺漏問題,增強了代碼的簡潔性和可讀性。
