<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 內存的申請[]() 通過前一小節我們可以知道,PHP底層對內存的管理,圍繞著小塊內存列表(free_buckets)、 大塊內存列表(large_free_buckets)和剩余內存列表(rest_buckets)三個列表來分層進行的。ZendMM向系統進行的內存申請,并不是有需要時向系統即時申請,而是由ZendMM的最底層(heap層)先向系統申請一大塊的內存,通過對上面三種列表的填充,建立一個類似于內存池的管理機制。在程序運行需要使用內存的時候,ZendMM會在內存池中分配相應的內存供使用。這樣做的好處是避免了PHP向系統頻繁的內存申請操作,如下面的代碼: <?php $tipi = "o_o\n"; [echo](http://www.php.net/echo) $tipi; ?> 這是一個簡單的php程序,但通過對emalloc的調用計數,發現對內存的請求有數百次之多,當然這非常容易解釋,因為PHP腳本的執行,需要大量的環境變量以及內部變量的定義,這些定義本身都是需要在內存中進行存儲的。 > 在編寫PHP的擴展時,推薦使用emalloc來代替malloc,其實也就是使用PHP的ZendMM來代替 手動直接調用系統級的內存管理。(除非,你自己知道自已在做什么。) 那么在上面這個小程序的執行過程中,ZendMM是如何使用自身的heap層存儲空間的呢?經過對源碼的追蹤我們可以找到: ZEND_ASSIGN_SPEC_CV_CONST_HANDLER (......) -> ALLOC_ZVAL(......) -> ZEND_FAST_ALLOC(......) -> emalloc (......) -> _emalloc(......) -> _zend_mm_alloc_int(.....) **_void *_emalloc_** 實現了對內存的申請操作,在_emalloc的處理過程中,對是否使用ZendMM進行了判斷,如果heap層沒有使用ZendMM來管理,就直接使用_zend_mm_heap結構中定義的_malloc函數進行內存的分配;(我們通過上節可以知道,這里的_malloc可以是malloc,win32,mmap_anon,mmap_zero中的一種); 就目前所知,不使用ZendMM進行內存管理,唯一的用途是打開enable-debug開關后,可以更方便的追蹤內存的使用情況。所以,在這里我們關注ZendMM使用_zend_mm_alloc_int函數進行內存分配: ![圖6.1 PHP內存管理器](http://box.kancloud.cn/2015-07-06_559a632d55546.jpg) 圖6.1 PHP內存管理器 結合上圖,再加上內存分配之前的驗證,ZendMM對內存分配的處理主要有以下步驟: 1. 內存檢查。 對要申請的內存大小進行檢查,如果太大(超出memory_limit則報 Out of Memory); 1. 如果命中緩存,使用fastcache得到內存塊(詳見第五節),然后直接進行第5步; 1. 在ZendMM管理的heap層存儲中搜索合適大小的內存塊, 在這一步驟ZendMM通過與ZEND_MM_MAX_SMALL_SIZE進行大小比較,把內存請求分為兩種類型: large和small。small類型的的請求會先使用zend_mm_low_bit函數在mm_heap中的free_buckets中查找,未找到則使用與large類型相同的方式:使用zend_mm_search_large_block函數在“大塊”內存(_zend_mm_heap->large_free_buckets)中進行查找。如果還沒有可以滿足大小需求的內存,最后在rest_buckets中進行查找。也就是說,內存的分配是在三種列表中小到大進行的。找到可以使用的block后,進行第5步; 1. 如果經過第3步的查找還沒有找到可以使用的資源(請求的內存過大),需要使用ZEND_MM_STORAGE_ALLOC函數向系統再申請一塊內存(大小至少為ZEND_MM_SEG_SIZE),然后直接將對齊后的地址分配給本次請求。跳到第6步; 1. 使用zend_mm_remove_from_free_list函數將已經使用block節點在zend_mm_free_block中移除; 1. 內存分配完畢,對zend_mm_heap結構中的各種標識型變量進行維護,包括large_free_buckets, peak,size等; 1. 返回分配的內存地址; 從上面的分配可以看出,PHP對內存的分配,是結合PHP的用途來設計的,PHP一般用于web應用程序的數據支持,單個腳本的運行周期一般比較短(最多達到秒級),內存大塊整塊的申請,自主進行小塊的分配,沒有進行比較復雜的不相臨地址的空閑內存合并,而是集中再次向系統請求。這樣做的好處就是運行速度會更快,缺點是隨著程序的運行時間的變長,內存的使用情況會“越來越多”(PHP5.2及更早版本)。所以PHP5.3之前的版本并不適合做為守護進程長期運行。(當然,可以有其他方法解決,而且在PHP5.3中引入了新的GC機制,詳見下一小節) ## 內存的銷毀[]() ZendMM在內存銷毀的處理上采用與內存申請相同的策略,當程序unset一個變量或者是其他的釋放行為時,ZendMM并不會直接立刻將內存交回給系統,而是只在自身維護的內存池中將其重新標識為可用,按照內存的大小整理到上面所說的三種列表(small,large,free)之中,以備下次內存申請時使用。 > 關于變量銷毀的處理,還涉及較多的其他操作,請參看[變量的創建和銷毀](#) 內存銷毀的最終實現函數是**_efree**。在**_efree**中,內存的銷毀首先要進行是否放回cache的判斷。如果內存的大小滿足ZEND_MM_SMALL_SIZE并且cache還沒有超過系統設置的ZEND_MM_CACHE_SIZE,那么,當前內存塊zend_mm_block就會被放回mm_heap->cache中。如果內存塊沒有被放回cache,則使用下面的代碼進行處理: zend_mm_block *mm_block; //要銷毀的內存塊 zend_mm_block *next_block; ... next_block = ZEND_MM_BLOCK_AT(mm_block, size); if (ZEND_MM_IS_FREE_BLOCK(next_block)) { zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); size += ZEND_MM_FREE_BLOCK_SIZE(next_block); } if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) { mm_block = ZEND_MM_PREV_BLOCK(mm_block); zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block); size += ZEND_MM_FREE_BLOCK_SIZE(mm_block); } if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(mm_block, size))) { zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE)); } else { ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size); zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block); } 這段代碼邏輯比較清晰,主要是根據當前要銷毀的內存塊**mm_block**在**zend_mm_heap**雙向鏈表中所處的位置進行不同的操作。如果下一個節點還是free的內存,則將下一個節點合并;如果上一相鄰節點內存塊為free,則合并到上一個節點;如果只是普通節點,剛使用 **zend_mm_add_to_free_list**或者**zend_mm_del_segment**進行回收。 就這樣,ZendMM將內存塊以整理收回到zend_mm_heap的方式,回收到內存池中。程序使用的所有內存,將在進程結束時統一交還給系統。 > 在內存的銷毀過程中,還涉及到引用計數和垃圾回收(GC),將在下一小節進行討論。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看