首頁 php教程 php手册 PHP原理之内存管理中难懂的几个点

PHP原理之内存管理中难懂的几个点

Jun 21, 2016 am 08:53 AM
free index size zend

  PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等.

  另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们平时开发扩展, 修复PHP的bug的时候, 却对这一部分的知识需要有一个良好的理解. PHP开发组内的很多朋友也对这块不是很清楚, 所以我觉得有必要专门写一下.

  一些基本的概念, 我就不赘述了, 因为看代码很容易能看懂, 我这里就主要介绍几个看代码没那么容易看懂的点, 为什么这么说呢, 呵呵, 我在写文章之前, 查找了下已有的资料, 已避免重复功, 其中看到了TIPI项目对这部分的描述, 发现其中错误很多. 所以, 我想这部分就是看代码也没那么容易看懂的点

  目前, 英文版的介绍也在撰写中: Zend MM

  Zend Memory Manager, 以下简称Zend MM, 是PHP中内存管理的逻辑. 其中有一个关键数据结构: zend_mm_heap:

  

 

  Zend MM把内存非为小块内存和大块内存俩种, 区别对待, 对于小块内存, 这部分是最最常用的, 所以追求高性能. 而对于大块内存, 则追求的是稳妥, 尽量避免内存浪费.

  所以, 对于小块内存, PHP还引入了cache机制:

  

 

  Zend MM 希望通过cache尽量做到, 一次定位就能查找分配.

  而一个不容易看懂的点是free_buckets的申明:

  Q: 为什么free_buckets数组的长度是ZEND_MM_NUMBER_BUCKET个?

  A: 这是因为, PHP在这处使用了一个技巧, 用一个定长的数组来存储ZEND_MM_NUMBER_BUCKET个zend_mm_free_block, 如上图中红色框所示. 对于一个没有被使用的free_buckets的元素, 唯一有用的数据结构就是next_free_block和prev_free_block, 所以, 为了节省内存, PHP并没有分配ZEND_MM_NUMBER_BUCKET * sizeof(zend_mm_free_block)大小的内存, 而只是用了ZEND_MM_NUMBER_BUCKET * (sizeof(*next_free_block) + sizeof(*prev_free_block))大小的内存..

  我们来看ZEND_MM_SMALL_FREE_BUCKET宏的定义:

 

  #define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \

  (zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \

  sizeof(zend_mm_free_block*) * 2 - \

  sizeof(zend_mm_small_free_block))

 

  之后, Zend MM 保证只会使用prev和next俩个指针, 所以不会造成内存读错..

  那么, 第二个不容易看懂的点, 就是PHP对large_free_buckets的管理, 先介绍分配(TIPI项目组对此部分的描述有些含糊不清):

 

  static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size)

 

  large_free_buckets可以说是一个建树和双向列表的结合:

  

 

  large_free_buckets使用一个宏来决定某个大小的内存, 落在什么index上:

 

  #define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S)

 

  zend_mm_high_bit获取true_size中最高位1的序号(zend_mm_high_bit), 对应的汇编指令是bsr(此处, TIPI项目错误的说明为: “这个hash函数用来计算size的位数,返回值为size二进码中1的个数-1″).

  也就是说, 每一个在large_free_buckets中的元素, 都保持着指向一个大小为在对应index处为1的size的内存块的指针. 诶, 有点绕口, 举个例子:

  比如对于large_free_buckets[2], 就只会保存, 大小在0b1000到0b1111大小的内存. 再比如: large_free_buckets[6], 就保存着大小为0b10000000到0b11111111大小的内存的指针.

  这样, 再分配内存的时候, Zend MM就可以快速定位到最可能适合的区域来查找. 提高性能.

  而, 每一个元素又同时是一个双向列表, 保持着同样size的内存块, 而左右孩子(child[0]和child[1])分别代表着键值0和1, 这个键值是指什么呢?

  我们来举个例子, 比如我向PHP申请一个true_size为0b11010大小的内存, 经过一番步骤以后, 没有找到合适的内存, PHP进入了zend_mm_search_large_block的逻辑来在large_free_buckets中寻找合适的内存:

  1. 首先, 计算true_size对应的index, 计算方法如之前描述的ZEND_MM_LARGE_BUCKET_INDEX

  2. 然后在一个位图结构中, 判断是否存在一个大于true_size的可用内存已经存在于large_free_buckets, 如果不存在就返回:

 

  size_t bitmap = heap->large_free_bitmap >> index;

  if (bitmap == 0) {

  return NULL;

  }

 

  3. 判断, free_buckets[index]是否存在可用的内存:

 

  if (UNEXPECTED((bitmap & 1) != 0))

 

  4. 如果存在, 则从free_buckets[index]开始, 寻找最合适的内存, 步骤如下:

  4.1. 从free_buckets[index]开始, 如果free_buckets[index]当前的内存大小和true_size相等, 则寻找结束, 成功返回.

  4.2. 查看true_size对应index后(true_size child[1]下面继续寻找, 如果free_buckets[index]->child[1]不存在, 则跳出. 如果true_size的当前最高位为0, 则在free_buckets[index]->child[0]下面继续寻找, 如果free_buckets[index]->child[0]不存在, 则在free_buckets[index]->child[1]下面寻找最小内存(因为此时可以保证, 在free_buckets[index]->child[1]下面的内存都是大于true_size的)

  4.3. 出发点变更为2中所述的child, 左移一位ture_size.

  5. 如果上述逻辑并没有找到合适的内存, 则寻找最小的”大块内存”:

 

  /* Search for smallest "large" free block */

  best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)];

  while ((p = p->child[p->child[0] != NULL])) {

  if (ZEND_MM_FREE_BLOCK_SIZE(p)

  best_fit = p;

  }

  }

 

  注意上面的逻辑, (p = p->child[p->child[0] != NULL]), PHP在尽量寻找最小的内存.

  为什么说, large_free_buckets是个键树呢, 从上面的逻辑我们可以看出, PHP把一个size, 按照二进制的01做键, 把内存大小信息反应到了键树上, 方便了快速查找.

  另外, 还有一个rest_buckets, 这个结构是个双向列表, 用来保存一些PHP分配后剩下的内存, 避免无意义的把剩余内存插入free_buckets带来的性能问题(此处, TIPI项目错误的描述为: “这是一个只有两个元素的数组。 而我们常用的插入和查找操作是针对第一个元素,即heap->rest_buckets[0]“).



本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

index.html是什麼文件? index.html是什麼文件? Feb 19, 2024 pm 01:36 PM

index.html代表網頁的首頁文件,是網站的預設頁面。當使用者造訪一個網站時,通常會先載入index.html頁面。 HTML(HypertextMarkupLanguage)是一種用來建立網頁的標記語言,index.html也是一種HTML檔。它包含網頁的結構和內容,以及用於格式化和佈局的標籤和元素。下面是一個範例的index.html程式碼:&lt

ChatGPT 現在允許免費用戶使用 DALL-E 3 產生每日限制的圖像 ChatGPT 現在允許免費用戶使用 DALL-E 3 產生每日限制的圖像 Aug 09, 2024 pm 09:37 PM

DALL-E 3 於 2023 年 9 月正式推出,是比其前身大幅改進的車型。它被認為是迄今為止最好的人工智慧圖像生成器之一,能夠創建具有複雜細節的圖像。然而,在推出時,它不包括

使用java的File.length()函數取得檔案的大小 使用java的File.length()函數取得檔案的大小 Jul 24, 2023 am 08:36 AM

使用Java的File.length()函數取得檔案的大小檔案大小是在處理檔案作業時很常見的一個需求,Java提供了一個很方便的方法來取得檔案的大小,即使用File類別的length()方法。本文將介紹如何使用此方法來取得檔案的大小,並給出對應的程式碼範例。首先,我們需要建立一個File物件來表示我們想要取得大小的檔案。以下是建立File物件的方法:Filef

PHP實作框架:Zend Framework入門教程 PHP實作框架:Zend Framework入門教程 Jun 19, 2023 am 08:09 AM

PHP實作框架:ZendFramework入門教學ZendFramework是PHP開發的開源網站框架,目前由ZendTechnologies維護,ZendFramework採用了MVC設計模式,提供了一系列可重複使用的程式碼庫,服務於實作Web2.0應用程式和Web服務。 ZendFramework深受PHP開發者的歡迎與推崇,擁有廣泛

如何在Zend框架中使用ACL(Access Control List)進行權限控制 如何在Zend框架中使用ACL(Access Control List)進行權限控制 Jul 29, 2023 am 09:24 AM

如何在Zend框架中使用ACL(AccessControlList)進行權限控制導言:在一個Web應用程式中,權限控制是至關重要的功能。它可以確保使用者只能存取其有權存取的頁面和功能,並防止未經授權的存取。 Zend框架提供了一種方便的方法來實現權限控制,即使用ACL(AccessControlList)元件。本文將介紹如何在Zend框架中使用ACL

Deepin V23 RC2 帶來大量調整和最佳化 Deepin V23 RC2 帶來大量調整和最佳化 Jun 28, 2024 am 07:45 AM

支援人工智慧的 Deepin Linux 發行版剛剛收到最新更新,稱為 V23 RC2。此版本的 Deepin 附帶了大量軟體包更新和新功能。但這不僅僅與功能有關。外觀和感覺是 t 的主要亮點

Linux中free指令有哪些進階用法? Linux中free指令有哪些進階用法? Feb 20, 2024 am 09:18 AM

在Linux系統中,free指令是一個重要的系統工具,用來監控系統記憶體的使用情況。它提供了查看記憶體總量、已使用量和可用量等資訊的基礎用法。此外,還有一些進階用法,如顯示詳細記憶體資訊、單位轉換和即時監控記憶體等功能。 free指令的基礎用法:free指令的基本語法如下:free[選項]以下是一些常用的選項:-h:以人類可讀的方式顯示記憶體大小。 -b:以位元組為單位顯示記憶體大小。 -k:以千位元組為單位顯示記憶體大小。 -m:以兆位元組為單位顯示記憶體大小。 -g:以吉字節為單位顯示記憶體大小。範例程式碼:讓我們透過範例程式碼

PHP原始碼運行問題:index報錯解決方法 PHP原始碼運行問題:index報錯解決方法 Mar 09, 2024 pm 09:24 PM

PHP原始碼運行問題:index報錯解決方法,需要具體程式碼範例PHP是一種廣泛使用的伺服器端腳本語言,經常被用於開發動態網站和Web應用程式。然而,有時候在運行PHP原始碼時會遇到各種問題,其中「index報錯」是比較常見的情況。本文將介紹一些常見的index報錯原因以及解決方法,並提供具體的程式碼範例,幫助讀者更好地處理這類問題。問題描述:在執行PHP程式時

See all articles