我编写了一个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中。这允许垃圾收集器完成它的工作,因为对象的引用计数都为零,因此可以从内存中删除。