<chsdate w:st="on" year="2007" month="4" day="10" islunardate="False" isrocdate="False"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt">2007 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">年</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt"> 4 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">月</span></chsdate> 10 日
了解如何使用 PHP V5.2 中引入的新内存管理器并开始精通于跟踪和监视内存使用情况。这将使您可以在 PHP V5.2 中更加有效地使用更多的内存。
PHP V5.2:开始
2006 年 11 月发布了 PHP V5.2,它包括许多新增功能和错误修正。它废止了 5.1 版并被推荐给所有 PHP V5 用户进行升级。我最喜欢的实验室环境 —— Windows®、Apache、MySQL、PHP (WAMP) —— 已经被引入了 V5.2 的新软件包中(请参阅 参考资料)。您将在那里找到在 Windows® XP 或 2003 计算机上安装 PHP V5.2、MySQL 和 Apache 的应用程序。您可以十分轻松地进行安装,它有很多不错的小的管理优点,并且我十分诚恳地推荐使用它。
虽然对于 Windows 用户来说,这是最简单的软件包,但是在 Linux 上配置 PHP 时您需要添加以下代码:--memory-limit-enabled(适用于您服务器的任何其他选项除外)。不过,在 Windows 下,提供了一个解决此问题的函数。
PHP V5.2 中有很多改进之处,并且一个至关重要的领域是内存管理。从 README.ZEND_MM 中准确地引述就是:“新内存管理器(PHP5.2 以及更高版本)的目标是减少内存分配开销并加速内存管理。”
下面是 V5.2 发行说明中的一些关键内容:
-
删除了不必要的 --disable-zend-memory-manager 配置选项
-
添加了 --enable-malloc-mm 配置选项,调试构建时此配置选项将被默认启用以允许使用内部和外部内存调试程序
-
允许使用 ZEND_MM_MEM_TYPE 和 ZEND_MM_SEG_SIZE 环境变量调整内存管理器
为了理解这些新增功能的含义,我们需要深入研究内存管理中的艺术,并考虑为什么分配开销和运行速度是大问题。
为什么进行内存管理?
计算中开发最快速的一项技术是内存和数据存储,它们是受不断增加速度和存储大小这样持续的需求而驱动的。早期的计算机使用卡作为内存,然后转向了芯片技术。您能想象在只有 1 KB RAM 内存的计算机工作的情景吗?很多早期的计算机程序员就曾使用过。这些先驱者很快就意识到,要在技术限制下工作,他们将必须细心地用琐碎的命令避免系统过载。
身为 PHP 开发人员,与使用 C++ 或其他更严格的语言编码的同事相比,我们所在的环境更方便进行编码。在我们的世界里,我们自己不必担心如何处理系统内存,因为 PHP 将为我们处理这个问题。但是,在其他编程领域里,负责任的编码人员将使用各种函数确保执行的命令不会覆盖其他一些程序数据 —— 因而,破坏了程序的运行。
内存管理通常是由来自编码人员的请求处理的,以分配和释放内存块。分配块 可以保存任何类型的数据,并且此过程将为该数据隔开一定量的内存,并当操作需要访问数据时为应用程序提供访问方法。人们期望程序在完成任何操作后释放分配的内存,并允许系统和其他程序员使用该内存。如果程序没有把内存释放回系统,则称为内存泄露。
泄露是任何运行程序都存在的普遍问题,并且某种程度内通常是可以接受的,尤其是当我们知道运行程序将立即终止并释放默认分配给程序的所有内存。
由于随机运行和终止程序,像几乎所有客户机应用程序一样,这是个问题。期望服务器应用程序不确定地运行而不终止或重新启动,这使得内存管理对于服务器守护程序编程绝对的至关重要。在长时间运行的程序中,即使一个小的泄露最后都将发展为系统衰弱问题,因为内存块已被使用并且永远不被释放。
长期考虑
正如使用任何语言编写一样,用 PHP 编写的永久性服务器守护程序有很多可能的用途。但是当我们出于这些目的开始使用 PHP 时,我们也必须考虑内存使用情况。
解析大量数据或可能隐藏无限次循环的脚本都趋于消耗大量内存。很明显,一旦内存被耗尽,服务器的性能就降低,因此在执行脚本时我们还必须注意内存的使用情况。虽然我们可以通过启用系统监视器来简单观察内存的使用量,但是它不会告诉我们比整个系统内存状态更有用的任何内容。有时我们不止需要帮助进行故障检修或优化的内容,而有时我们只是需要更多详细信息。
获得脚本执行内容的透明性的一种方法是使用内部或外部调试器。内部调试器 是呈现为执行脚本的相同的进程。从操作系统的角度考虑的独立进程是外部调试器。使用调试器进行内存分析类似于任何一种情况,但是使用了不同的方法访问内存。内部调试器对运行进程所在的内存空间具有直接访问权,而外部调试器将通过套接字访问内存。
有许多方法和可用的调试服务器(外部)和库(内部)可用于辅助开发。为了准备好对 PHP 安装进行调试,可以使用新提供的 --enable-malloc-mm,它在 DEBUG 构建中默认被启用。这使环境变量 USE_ZEND_ALLOC 可用于允许在运行时选择 malloc 或 emalloc 内存分配。使用 malloc-type 内存分配将允许外部调试器观察内存使用情况,而 emalloc 分配将使用 Zend 内存管理器抽象,要求进行内部调试。
PHP 中的内存管理函数
除了使内存管理器更灵活更透明之外,PHP V5.2 还为 memory_get_usage() 和 memory_get_peak_usage() 提供了一个新参数,这两个函数允许查看内存使用量。说明中提及的新布尔值是 real_size。通过调用函数 memory_get_usage($real);(其中 $real = true),结果将为调用时系统中实际分配的内存大小,包括内存管理器开销。如果不使用标记组,则返回的数据将只包括在运行脚本内使用的内存,减去内存管理器开销。
memory_get_usage() 和 memory_get_peak_usage() 的不同之处在于后者将返回到目前为止调用它的运行进程的最高内存量,而前者只返回执行时的使用量。
对于 memory_get_usage(),php.net 提供了清单 1 中的代码片段。
清单 1. memory_get_usage() 示例
<?php
// This is only an example, the numbers below will
// differ depending on your system
echo memory_get_usage() . "\n"; // 36640
$a = str_repeat("Hello", 4242);
echo memory_get_usage() . "\n"; // 57960
unset($a);
echo memory_get_usage() . "\n"; // 36744
?>
|
在这个简单示例中,我们首先回转了直接调用 memory_get_usage() 的结果,代码注释中显示可能在作者的系统中有 36640 字节的常见结果。然后我们使用 4,242 个 “Hello” 副本来装载 $a 并再次运行函数。
图 1. memory_get_usage() 的示例输出
没有 memory_get_peak_usage() 的示例,因为两者十分相似,语法是相同的。但是,对于清单 1 中的示例代码,将只有一个结果,即当时的最高内存使用量。让我们看一看清单 2。
清单 2. memory_get_peak_usage() 示例
<?php
// This is only an example, the numbers below will
// differ depending on your system
echo memory_get_peak_usage() . "\n"; // 36640
$a = str_repeat("Hello", 4242);
echo memory_get_peak_usage() . "\n"; // 57960
unset($a);
echo memory_get_peak_usage() . "\n"; // 36744
?>
|
清单 2 中的代码跟图 1 一样,但是 memory_get_usage() 已经替换为 memory_get_peak_usage()。在我们用 4242 个 “Hello” 副本填充 $a 之前,输出都不会有多大更改。内存跳升至 57960,表示到目前为止的峰值。当检查内存使用量峰值时,得到了目前为止的最高值,因此所有进一步调用都将得到 57960,直至我们处理的操作比处理 $a 使用的内存更多
图 2. memory_get_peak_usage() 的示例输出
限制内存使用
确保托管应用程序的服务器不过载的一种方法是限制 PHP 执行的任何脚本使用的内存量。这根本不是我们应当执行的操作,但由于 PHP 是一种松散类型的语言,并且是在运行时解析的,因此我们有时会获得在释放到生产应用程序中后编写得很差的脚本。这些脚本可能执行循环,也可能打开一张长的文件列表,忘记在打开新文件之前先关闭当前文件。无论在哪一种情况下,编写很差的脚本可能在您知道之前以消耗大量内存告终。
在 PHP.INI 中,您可以使用配制参数 memory_limit 来指定任何脚本能够在系统中运行的最大内存使用量。这不是对于 V5.2 的特定更改,但是内存管理器及其使用的任何讨论都值得至少快速查看一次这个特性。它还精心地引导我使用内存管理器的最后几个新功能:环境变量。
调整内存管理器
最后,在不能做完美主义者但是又完全符合自己目的的情况下怎样编程?新环境变量 ZEND_MM_MEM_TYPE 和 ZEND_MM_SEG_SIZE 正好可以满足您的需求。
当内存管理器分配大型内存块时,它是安装 ZEND_MM_SEG_SIZE 变量中列出的预定大小执行操作的。这些内存块的默认分区大小为每块 256 KB,但是您可以调整这些分区大小以满足特殊需求。例如,如果您注意到最常用的一个脚本中的操作导致大量的内存浪费,则可以将此大小调整为更接近匹配脚本需求的值,减少分配的内存量但剩下的内存量仍然为零。在正确的条件下,此类谨慎的配制调整可能造成巨大差别。
在 Windows 中检索内存使用情况
如果具有预构建的 PHP Windows 二进制代码,而没有在构建时使用 --enable-memory-limit 选项,则需要先浏览此部分然后再继续。对于 Linux®,配置 PHP 构建时用 --enable-memory-limit 选项构建 PHP。
要使用 Windows 二进制代码检索内存使用情况,请创建以下函数。
清单 3. 在 Windows 下获得内存使用情况
<?php
function memory_get_usage(){
$output = array();
exec('tasklist /FI "PID eq '.getmypid().'" /FO LIST', $output );
return preg_replace( '/[^0-9]/', '', $output[5] ) * 1024;
}
?>
|
将结果保存到名为 function.php 的文件。现在您只能将此文件包含在需要使用它的脚本中。
动手实践
让我们来看一看使用这些设置的实际示例给我们带来的好处。可能有很多次您都想知道为什么在脚本的末尾没有正确分配内存。原因是因为一些函数本身导致了内存泄露,尤其是在仅使用内置 PHP 函数的情况下。在这里,您将了解如何发现此类问题。并且为了开始进行内存泄露查找的征战,您将创建一个测试 MySQL 数据库,如清单 4 所示。
清单 4. 创建测试数据库
mysql> create database memory_test;
mysql> use memory_test;
mysql> create table leak_test
( id int not null primary key auto_increment,
data varchar(255) not null default '');
mysql> insert into leak_test (data) values ("data1"),("data 2"),
("data 3"),("data 4"),("data 5"),("data6"),("data 7"),
("data 8"),("data 9"),("data 10");
|
这将创建一个带有 ID 字段和数据字段的简单表。
在下一张清单中,想象我们坚韧不拔的程序员正在执行一些 MySQL 函数,特别是使用 mysql_query() 将结果应用到变量。当他这样做时,他将注意到即使调用 mysql_free_result(),一些内存也不会被释放,导致内存使用量随着 Apache 进程不断增长(参见清单 5)。
清单 5. 内存泄露检测示例
for ( $x=0; $x<300; $x++ ) {
$db = mysql_connect("localhost", "root", "test");
mysql_select_db("test");
$sql = "SELECT data FROM test";
$result = mysql_query($sql); // The operation suspected of leaking
mysql_free_result($result);
mysql_close($db);
}
|
清单 5 是在任何位置都可能使用的简单 MySQL 数据库操作。在运行脚本时,我们注意到一些与内存使用量相关的奇怪行为并需要将其检查出来。为了使用内存管理函数以使我们可以检验发生错误的位置,我们将使用以下代码。
清单 6. 定标查找错误的示例
<?php
if( !function_exists('memory_get_usage') ){
include('function.php');
}
echo "At the start we're using (in bytes): ",
memory_get_usage() , "\n<br>";
$db = mysql_connect("localhost", "user", "password");
mysql_select_db("memory_test");
echo "After connecting, we're using (in bytes): ",
memory_get_usage(),"\n<br>";
for ( $x=0; $x<10; $x++ ) {
$sql =
"SELECT data FROM leak_test WHERE id='".$x."'";
$result = mysql_query($sql); // The operation
// suspected of leaking.
echo "After query #$x, we're using (in bytes): ",
memory_get_usage(), "\n<br>";
mysql_free_result($result);
echo "After freeing result $x, we're using (in bytes): ",
memory_get_usage(), "\n<br>";
}
mysql_close($db);
echo "After closing the connection, we're using (in bytes): ",
memory_get_usage(), "\n<br>";
echo "Peak memory usage for the script (in bytes):".
memory_get_peak_usage();
?>
|
注:按照定义的时间间隔检查当前内存使用量。在下面的输出中,通过显示我们的脚本一直在为函数分配内存,并且在应当释放的时候没有释放内存,从而提供对内存泄露的
分享到:
相关推荐
UPUPW APACHE PHP5.2.17经典怀旧版为经典源码而生,主要适应PHP5.2系列版本用Zend加密过的程序,完美运行ShopEx/ECShop/phpweb等PHP源码。 优势特点: 1、绿色免费,无需安装,不写入任何注册表信息,下载集成包...
1、缓存支持内存缓存,如:memcache 缓存; 2、不重复生成和检查模版缓存,提升性能; 五、移动优先设计: 1、默认支持移动端访问自适应; 2、无需重复发文章,文章一键推送。 相关阅读...
UPUPW PHP探针通用版,优势:支持最新PHP7.0系列版本,完全兼容PHP5.2-PHP5.6组件和参数的检测,防XSS漏洞攻击。 UPUPW PHP探针通用版功能列表: 01、支持服务器域名及端口检测。 02、支持服务器环境webserver检测...
PHP中可以使用`array_merge()`,`+`运算符或`array_combine()`(如果需要合并键值对数组)等方式合并两个数组。`array_merge()`会将两个数组的元素合并,如果有相同键,则后面的数组值会覆盖前面的;`+`运算符则...
挖掘鸡x3.0豪华版2012最新版,挖掘鸡是一些黑客及黑客软件(包括网站管理员及管理员工具)会在网站生成特定路径(目录名+文件名),这些路径往往有习惯性及默认性。 这样的路径在网络中孤立无链接,通过搜索引擎很难...
例如,如果你正在使用Laravel 5.2,那么DebugBar v5.2.2应该是一个合适的版本。 2. **安装与配置**:按照官方文档或相关教程进行安装,通常通过Composer(PHP依赖管理工具)来添加DebugBar到项目中,并在配置文件中...
重点说明:分类管理中[功能帮助]类别,初始建站都会遇到帮助中心的分类页无法更新生成为新网站信息,那么如何操作呢?首先进入该类,里面有个人资料完善和管理、沟通与交流等分类,然后全部选定各个分类,选择生成...
Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。 设定字符串为“张三,你好,我是李四”...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...
ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...