在Mac上使用MAMP v2.0 __ Apache/2.0.64 (Unix) -- PHP/5.3.5 -- DAV/2 mod_ssl/2.0.64 -- OpenSSL/0.9.7l -- MySQL 5.5.9。
我有一个脚本,我试图运行它,但它似乎给我造成了严重的内存泄漏问题,尝试过调试,但无法解决。
基本上,该脚本是文件管理器模块的一部分。当给定ID时,它会处理文件的下载。
整个文件以64kb块(每个记录)的BLOB形式存储在数据库表中,并在请求时流式传输到客户端。
引用:
数据库:file_management 表:file_details, file_data file_details:
FileID - int(10) AUTO_INCREMENT
FileTypeID - int(10)
FileType - varchar(60)
FileName - varchar(255)
FileDescription - varchar(255)
FileSize - bigint(20)
FileUploadDate - datetime
FileUploadBy - int(5)
file_details:
FileDataID - int(10) AUTO_INCREMENT
FileID - int(10)
FileData - BLOB
实际收到的错误是(来自php错误日志): [31-Oct-2011 09:47:39] PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 63326173 bytes) in /root/htdocs/file_manager/file_manager_download.php on line 150 现在,如果文件足够小,实际下载功能是正常的,例如在这种情况下,小于40mb,但是一旦超过这个大小,就像上面错误中的60mb文件一样,它就会失败。它只是下载一个0kb的文件。
显然,134217728字节比63326173字节(128mb vs 60mb)多。 Allowed memory size of 134217728 bytes 是php.ini中的指令:"memory_limit = 128M ; Maximum amount of memory a script may consume" 如果我将其设置为256M,则允许我下载那个60mb文件,以及高达80mb左右的文件。
此外,如果我将其设置为1024M,则允许我下载260mb文件,可能更大。
因此,您可以看到问题是脚本中某个地方存在泄漏,导致所有内存被耗尽。
以下是下载脚本:
我有一个脚本,我试图运行它,但它似乎给我造成了严重的内存泄漏问题,尝试过调试,但无法解决。
基本上,该脚本是文件管理器模块的一部分。当给定ID时,它会处理文件的下载。
整个文件以64kb块(每个记录)的BLOB形式存储在数据库表中,并在请求时流式传输到客户端。
引用:
数据库:file_management 表:file_details, file_data file_details:
FileID - int(10) AUTO_INCREMENT
FileTypeID - int(10)
FileType - varchar(60)
FileName - varchar(255)
FileDescription - varchar(255)
FileSize - bigint(20)
FileUploadDate - datetime
FileUploadBy - int(5)
file_details:
FileDataID - int(10) AUTO_INCREMENT
FileID - int(10)
FileData - BLOB
实际收到的错误是(来自php错误日志): [31-Oct-2011 09:47:39] PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 63326173 bytes) in /root/htdocs/file_manager/file_manager_download.php on line 150 现在,如果文件足够小,实际下载功能是正常的,例如在这种情况下,小于40mb,但是一旦超过这个大小,就像上面错误中的60mb文件一样,它就会失败。它只是下载一个0kb的文件。
显然,134217728字节比63326173字节(128mb vs 60mb)多。 Allowed memory size of 134217728 bytes 是php.ini中的指令:"memory_limit = 128M ; Maximum amount of memory a script may consume" 如果我将其设置为256M,则允许我下载那个60mb文件,以及高达80mb左右的文件。
此外,如果我将其设置为1024M,则允许我下载260mb文件,可能更大。
因此,您可以看到问题是脚本中某个地方存在泄漏,导致所有内存被耗尽。
以下是下载脚本:
ini_set('display_errors',1);
error_reporting(E_ALL & ~E_NOTICE);
$strDB=mysql_connect("localhost","username","password")or die ("无法连接mysql.. 错误: (" . mysql_errno() . ") " . mysql_error());
$database=mysql_select_db("file_management",$strDB);
if (isset($_GET["id"])) {
// 用于表示每个64kb块的节点列表
$nodelist = array();
// 获取文件元数据
$sql_GetFileDetails = "
SELECT
FileID,
FileTypeID,
FileType,
FileName,
FileDescription,
FileSize,
FileUploadDate,
FileUploadBy
FROM file_details WHERE FileID = '".$_GET["id"]."';";
$result_GetFileDetails = mysql_query($sql_GetFileDetails) or die ("没有找到此文件ID相关的结果。
查询请求: " . $sql_GetFileDetails . "
错误: (" . mysql_errno() . ") " . mysql_error());
if (mysql_num_rows($result_GetFileDetails) != 1) { die ("发生MySQL错误。
查询请求: " . $sql_GetFileDetails . "
错误: (" . mysql_errno() . ") " . mysql_error()); }
// 设置文件对象以获取详细信息
$FileDetailsArray = mysql_fetch_assoc($result_GetFileDetails);
// 获取文件inode列表
$sql_GetFileDataNodeIDs = "SELECT FileDataID FROM file_data WHERE FileID = ".$_GET["id"]." order by FileDataID";
if (!$result_GetFileDataNodeIDs = mysql_query($sql_GetFileDataNodeIDs)) { die("未能检索文件inode列表
查询请求: " . $sql_GetFileDataNodeIDs . "
错误: (" . mysql_errno() . ") " . mysql_error()); }
while ($row_GetFileDataNodeIDs = mysql_fetch_assoc($result_GetFileDataNodeIDs)) {
$nodelist[] = $row_GetFileDataNodeIDs["FileDataID"];
}
$FileExtension = explode(".",$FileDetailsArray["FileName"]);
$FileExtension = strtolower($FileExtension[1]);
// 确定内容类型
switch ($FileExtension) {
case "mp3": $ctype="audio/mp3"; break;
case "wav": $ctype="audio/wav"; break;
case "pdf": $ctype="application/pdf"; break;
//case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="application/force-download"; break; // 这将强制下载,而不是在浏览器中查看。
case "png": $ctype="application/force-download"; break; // 这将强制下载,而不是在浏览器中查看。
case "jpeg": $ctype="application/force-download"; break; // 这将强制下载,而不是在浏览器中查看。
case "jpg": $ctype="application/force-download"; break; // 这将强制下载,而不是在浏览器中查看。
default: $ctype="application/force-download"; // 这将强制下载,而不是在浏览器中查看。
}
// 发送头信息到客户端
header("Date: ".gmdate("D, j M Y H:i:s e", time()));
header("Cache-Control: max-age=2592000");
//header("Last-Modified: ".gmdate("D, j M Y H:i:s e", $info['mtime']));
//header("Etag: ".sprintf("\"%x-%x-%x\"", $info['ino'], $info['size'], $info['mtime']));
header("Accept-Ranges: bytes");
//header("Cache-Control: Expires ".gmdate("D, j M Y H:i:s e", $info['mtime']+2592000));
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header
我使用了Xdebug并输出了峰值内存使用情况,但似乎没有任何东西接近极限,总的来说,该页面的峰值内存使用量大约为900kb。
因此,我认为它正在将文件块聚合到内存中并且不释放它们,或者类似的问题,但是文件块是唯一会达到那个内存量并导致脚本失败的东西。
如果您愿意,我可以提供上传文件到数据库的脚本,这样您就可以测试我的脚本,只需让我知道即可
感谢任何帮助!
Mick
*/////////已解决/////////*
我想对hafichuk表示感谢,他的回复很好,并解决了我的整个问题。
问题有两个方面。
1-我没有在while循环中使用ob_flush()。我添加了它,它似乎释放了大量内存,使得更大的下载成为可能,但不是无限制的。
例如,使用memory_limit = 128M,我现在可以下载超过40mb,实际上我现在可以获得大约200mb的下载速度。但这就是它再次失败的地方。首先解决了内存问题。
教训1:刷新你的对象!
2-我正在使用mysql_query来检索SQL查询的结果。问题在于它缓冲这些结果,这增加了我的内存限制问题。
最后我使用了mysql_unbuffered_query,现在它完美运行。
但是,这确实带来了一些限制,即在读取结果时会锁定您的表。
教训2:如果不需要,请勿缓冲mysql结果!(在程序限制范围内)
最终教训:
所有这些修复方法都有效,但需要进行更多的测试以确保它们之间没有问题。
此外,我对对象和php内存分配有了更多的了解,但我希望有一种比xdebug提供的更好的方式来可视化调试该过程。如果有人对xdebug如何可以实际阐明此过程的任何想法,请在评论中让我知道。
希望这能帮助未来的某个人。
干杯
Mick