从某个意义上讲,资源总是有限的,计算机资源也是如此,衡量一个计算机处理能力的指标有很多,根据不同的应用需要也会有不同的指标,比如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中文手册