從某個意義上講,資源總是有限的,計算機資源也是如此,衡量一個計算機處理能力的指標有很多,根據不同的應用需要也會有不同的指標,比如3D游戲對顯卡的性能有要求,而Web服務器對吞吐量及響應時間有要求,通常CPU、內存及硬盤的讀取和計算速度具有決定性的作用,在同一時刻這些資源是有限的,正是因為有限我們才需要合理的利用他們。
## 操作系統的內存管理[]()
當計算機的電源被打開之后,不管你使用的是什么操作系統,這些軟件可能已經在使用內存了。這是由計算機的結構決定的,操作系統也是一種軟件,只不過它是比較特殊的軟件,管理計算機的所有資源,普通應用程序和操作系統的關系有點像老師和學生,老師通常管理一切,而學生的行為會受到老師或學校規定的限制,例如普通應用程序無法直接訪問物理內存或者其他硬件資源。
操作系統直接管理著內存,所以操作系統也需要進行內存管理,內存管理是如此之重要,計算機中通常都有[內存管理單元(MMU)](http://zh.wikipedia.org/wiki/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E5%8D%95%E5%85%83)用于處理CPU對內存的訪問。
## 應用層的內存管理[]()
由于計算機的內存由操作系統進行管理,所以普通應用程序是無法直接對內存進行訪問的,應用程序只能向操作系統申請內存,通常的應用也是這么做的,在需要的時候通過類似malloc之類的庫函數向操作系統申請內存,在一些對性能要求較高的應用場景下是需要頻繁的使用和釋放內存的,比如Web服務器,編程語言等,由于向操作系統申請內存空間會引發[系統調用](http://zh.wikipedia.org/wiki/%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8),系統調用和普通的應用層函數調用性能差別非常大,因為系統調用會將CPU從用戶態切換到內核,因為涉及到物理內存的操作,只有操作系統才能進行,而這種切換的成本是非常大的,如果頻繁的在內核態和用戶態之間切換會產生性能問題。
鑒于系統調用的開銷,一些對性能有要求的應用通常會自己在用戶態進行內存管理,例如第一次申請稍大的內存留著備用,而使用完釋放的內存并不是馬上歸還給操作系統,可以將內存進行復用,這樣可以避免多次的內存申請和釋放所帶來的性能消耗。
PHP不需要顯式的對內存進行管理,這些工作都由Zend引擎進行管理了。PHP內部有一個內存管理體系,它會自動將不再使用的內存垃圾進行釋放,這部分的內容后面的小節會介紹到。
## PHP中內存相關的功能特性[]()
可能有很多的讀者碰到過類似下面的錯誤吧:
Fatal error: Allowed memory size of X bytes exhausted (tried to allocate Y bytes)
這個錯誤的信息很明確,PHP已經達到了允許使用的最大內存了,通常上來說這很有可能是我們的程序編寫的有些問題。比如:一次性讀取超大的文件到內存中,或者出現超大的數組,或者在大循環中的沒有及時是放掉不再使用的變量,這些都有可能會造成內存占用過大而被終止。
PHP默認的最大內存使用大小是32M, 如果你真的需要使用超過32M的內存可以修改php.ini配置文件的如下配置:
memory_limit = 32M
如果你無法修改php配置文件,如果你的PHP環境沒有禁用ini_set()函數,也可以動態的修改最大的內存占用大小:
<?php
ini_set("memory_limit", "128M");
既然我們能動態的調整最大的內存占用,那我們是否有辦法獲取目前的內存占用情況呢?答案是肯定的。
1. [memory_get_usage()](http://www.php.net/manual/en/function.memory-get-usage.php),這個函數的作用是獲取目前PHP腳本所用的內存大小。
1. [memory_get_peak_usage()](http://www.php.net/manual/en/function.memory-get-peak-usage.php),這個函數的作用返回當前腳本到目前位置所占用的內存峰值,這樣就可能獲取到目前的腳本的內存需求情況。
單就PHP用戶空間提供的功能來說,我們似乎無法控制內存的使用,只能被動的獲取內存的占用情況,這樣的話我們學習內存管理有什么用呢?
前面的章節有介紹到引用計數,函數表,符號表,常量表等。當我們明白這些信息都會占用內存的時候,我們可以有意的避免不必要的浪費內存,比如我們在項目中通常會使用autoload來避免一次性把不一定會使用的類包含進來,而這些信息是會占用內存的,如果我們及時把不再使用的變量unset掉之后_可能_會釋放掉它所占用的空間,
> 前面之所以會說把變量unset掉時候_可能_會把它釋放掉的原因是: 在PHP中為了避免不必要的內存復制,采用了引用計數和寫時復制的技術, 所以這里unset只是將引用關系打破,如果還有其他變量指向該內存, 它所占用的內存還是不會被釋放的。
當然這還有一種情況:出現循環引用,這個就得靠gc來處理了, 內存不會當時就是放,只有在gc環節才會被釋放。
后面的章節主要介紹PHP在運行時的內存使用和管理細節。這也能幫助我們寫出更為內存友好的PHP代碼。
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊