PHP:使用unset()或赋值为null后内存未释放


PHP: Memory not being freed after using of unset() or assignment to null

我编写了一个PHP插件,它将记录从Endnote XML参考书目文件导入数据库。导入过程包括几个阶段,其中一个阶段是将尾注记录初始读取到内存中,并为它们创建一个基于对象的内部表示。其次,所有要导入的记录都必须进行扫描,以确定其作者、出版物、关键字等是否已经在数据库中有相应的记录。

我在OS X 10.8.2上运行PHP 5.4.7(64位)。

为了快速完成这些任务,我将几乎所有的数据存储在内存中,而不是将数据写入数据库或反复查阅数据库。。。所有必要的数据都会被读入一次,并根据需要进行查阅。

这当然是记忆密集型的。然而,我已经开发了许多减少内存占用的策略,这些策略在减少使用的内存量方面非常有效。特别是,我大量使用了原生PHP序列化/非序列化功能,以及zlib来压缩序列化的表示。尽管如此,内存使用率仍然很高,在只导入500条尾注记录后,最大内存就会耗尽。

为了解决这个问题,我尝试使用unset()内部函数来释放所有不再需要的变量、数组和对象。其中一些对象在实例化时相当大,这就是为什么我会尽快处理它们。然而,在进行了一些内存使用情况分析后,我发现memory_get_usage(true)报告的内存使用情况并没有下降,尽管unset处理变量,使用gc_enable()启用垃圾收集,并通过gc_collect_cycles()请求定期垃圾收集。

我读过其他帖子,这些帖子表明,只要特定变量的引用计数没有降到零,PHP就不会释放相关的内存,我理解这一点。我已经设计了我的代码来避免循环引用。。。每个不同的内存消耗对象都有一组独立的内部存储阵列,其中没有一个与其他对象共享数据。因此,在销毁主机对象时,理论上,它的所有私有数据都应该立即释放。然而,我没有看到这种情况发生。

如果有人想看看我的代码,可以在Github上找到。

1.在文件/test/ObjectStoreTest.php 中可以找到主单元测试,该测试对各种高内存使用率对象进行测试,并测量内存使用率(处理500条尾注记录,总共使用122MB)

2./controller/ParseAndStoreEndnoteRecordsHandler.class.php 中可以找到解析尾注数据并将其转换为基于对象的表示(运行期间使用约15MB)的例程

3./model/resolvers/CreatorExternalReferenceResolver.class.php 中可以找到用于发现主数据库中已有作者的类(似乎占用了大约30MB)

我提供这些信息供参考,以备回答问题时需要。。。很明显,我不希望任何人花半天时间分析我的代码。但是,希望这些信息足以清楚地说明我遇到的特定内存使用问题。

您看到的问题是由于PHP的垃圾回收器尚未启动,或者内存消耗对象的引用计数尚未为零。GC更有可能使用较低的内存限制,但看起来您需要所有的内存空间。我会将内存限制设置为"原样"或更高,然后让发动机完成它的工作。

唯一真正的解决方案是使用升级到PHP 5.5.0的alpha版本,并使用该版本中的Generators或Co例程来减少内存占用。它只允许您查看有问题对象的值,当它移动到下一个对象时,不会将该值保存在RAM中。这允许垃圾收集器完成它的工作,因为对象的引用计数都为零,因此可以从内存中删除。