1

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

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

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

这当然是内存密集型的。但是,我已经制定了许多减少内存占用的策略,这些策略在减少使用的内存量方面非常有效。特别是,我广泛使用原生 PHP 序列化/反序列化工具以及 zlib 来压缩序列化表示。尽管如此,内存使用率仍然高得令人不安,仅导入 500 条 Endnote 记录后就耗尽了最大内存。

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

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

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

1.在文件中找到了对各种高内存使用对象进行测试的主要单元测试,并测量内存使用情况(处理 500 条 Endnote 记录,总共使用 122MB)/test/ObjectStoreTest.php

2.解析 Endnote 数据并将其转换为基于对象的表示的例程(在运行期间使用大约 15MB)在/controller/ParseAndStoreEndnoteRecordsHandler.class.php

3.发现主库已有作者的类(好像用了30MB左右)在/model/resolvers/CreatorExternalReferenceResolver.class.php

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

4

1 回答 1

1

The problem your seeing is due to the fact that that PHP's Garbage Collector has not kicked in yet, or the reference count for the memory consuming objects are not zero yet. The GC is more likely to kick in with a lower memory limit, but it looks like you need all of that memory space. I would set the memory limit 'as is' or higher and let the engine do its job.

The only true fix for this is to use to upgrade to the alpha version of PHP 5.5.0 and use Generators or Co-routines found in that build to keep the memory foot print down. It allows you to only peek at the value of the object in question and does not keep that value in RAM when it moves onto the next object. This allows for the garbage collector to do it's job as the reference counts for the objects are all zero and can thus be removed from memory.

于 2013-02-15T05:01:56.177 回答