【问题标题】:PHP / JpGraph - Memory not freed on completion of functionPHP / JpGraph - 功能完成后未释放内存
【发布时间】:2012-10-26 12:47:31
【问题描述】:

我的应用程序需要生成一个包含一堆图表的 pdf 文件。

情况:我使用 FPDF 生成 pdf,使用 JpGraph 生成图表。我的代码从数据库中获取图形数据并进行迭代,为每个包含所有 JpGraph 代码的图形调用一个函数,用于设置、设置图形样式并将其作为 png 文件缓存在服务器上的缓存文件夹中。 FPDF 然后将这些图像放入 pdf 中,然后提供给浏览器。

问题:当图表数量超过一定数量时,我会遇到 PHP 内存不足错误。 AFAICT 这不是 FPDF 问题:在尝试诊断问题时,我生成了更大的文档,其中包含更多(预生成的)图形和同等大小的图像。问题似乎是在图形渲染函数中用于渲染图形的内存在函数完成时没有被释放。这是基于这样一个事实,如果我在函数中调用 memory_get_peak_usage,我会得到一堆不断增加的数字,每次调用函数时都会有一个数字,直到它停止时的限制为 64MB。

我的图形生成脚本如下所示:

function barChart($filename, $ydata, $xdata){

// Create the graph. These two calls are always required
$graph = new Graph(900,500);
$graph->SetScale('textlin');

//(bunch of styling stuff)

// Create the bar plot
$bplot=new BarPlot($ydata);

// Add the plot to the graph
$graph->Add($bplot);

//(more styling stuff)


// Display the graph
$graph->Stroke($filename);

$graph = null;
$bplot = null;

unset($graph);
unset($bplot);

echo "<br><br>".(memory_get_peak_usage(true)/1048576)."<br><br>";
}

如您所见,我尝试取消设置和取消 graph 和 bplot 对象,尽管我的理解是这不应该是必要的。当函数完成时,不应该释放 Graph 和 Bplot 实例使用的所有内存吗?或者这可能是 JpGraph 内存泄漏? (我到处搜索,找不到其他人抱怨这个)。这是我的第一个远程资源密集型 PHP 项目,所以我可能会遗漏一些明显的东西。

【问题讨论】:

  • 你得到了我对这个问题的支持,因为它对我解决同样的问题有很大帮助(我使用的是 PHP 7)。我无法在任何地方找到解决方案,只能在这里。使用memory_get_peak_usage(true) 对调查由 JpGraph 产生的内存泄漏非常有帮助。

标签: php out-of-memory jpgraph


【解决方案1】:

我遇到了同样的问题,一个小时左右就找到了解决方案。

问题在于 jpgraph 每次创建图表时都会加载一组默认字体文件。我找不到卸载字体的方法,所以我做了一点改动,让它只加载一次字体。

要修复您的安装,请按如下方式编辑“gd_image.inc.php”:

在文件开头附近的某处添加以下内容(就在 CLASS 图像之前):

// load fonts only once, and define a constant for them
define("GD_FF_FONT0", imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT0.gdf"));
define("GD_FF_FONT1", imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1.gdf"));
define("GD_FF_FONT2", imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2.gdf"));
define("GD_FF_FONT1_BOLD", imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1-Bold.gdf"));
define("GD_FF_FONT2_BOLD", imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2-Bold.gdf"));

然后在 Image 类构造函数的末尾(第 91-95 行),替换为:

$this->ff_font0 =  imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT0.gdf");
$this->ff_font1 =  imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1.gdf");
$this->ff_font2 =  imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2.gdf");
$this->ff_font1_bold =  imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1-Bold.gdf");
$this->ff_font2_bold =  imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2-Bold.gdf");

用这个:

$this->ff_font0 =  GD_FF_FONT0;
$this->ff_font1 =  GD_FF_FONT1;
$this->ff_font2 =  GD_FF_FONT2;
$this->ff_font1_bold =  GD_FF_FONT1_BOLD;
$this->ff_font2_bold =  GD_FF_FONT2_BOLD;

我没有使用多个版本的 php 或 jpgraph 对此进行测试,但它应该可以正常工作。 ymmv。

【讨论】:

  • 这是一个绝妙的答案......在 2019 年找到了很多用处 @bobD。遗憾的是 jGraph 没有得到维护。我可以确认,通过这些更改,大多数用例的内存泄漏为零。
  • 我遇到了同样的内存泄漏问题。我必须用 JpGraph 生成的多个图像创建一个 PDF 文档。达到 128MB 后,一切都卡住了,我不得不仔细调查这个问题。 JpGraph 似乎没有很好地进行垃圾收集,并且几个图像类正在填充内存而不清除它。因此,在每次生成图像后,我都必须使用类($foo = new Image(...); 和更高版本:unset($foo);)取消设置变量,另外还要使用 PHP 的 gc_collect_cycles() + 我使用你的方法只加载一次字体。最后我修复了内存泄漏。
【解决方案2】:

您可以尝试使用 PHP >= 5.3 垃圾收集

gc_enable() + gc_collect_cycles()

http://php.net/manual/en/features.gc.php

【讨论】:

  • 感谢您的想法 - 我的生产服务器正在(并且必须)运行 5.2,但我已经在相同的内存限制下使用 5.3 在本地进行了测试,新的 gc 东西似乎没有帮助。我猜它要么调试 JpGraph 中明显糟糕的内存处理,通过运行多个线程来破解它,或者更改图形系统......
【解决方案3】:

@bobD 的回答是正确的,并帮助解决了我同样的问题。

但是,对于那些仍在寻找这个非常古老问题的答案的人来说,还有另一个潜在的内存泄漏源。

如果您使用相同的背景图像创建多个图表,则每次加载背景图像都会导致每次创建图表时内存增加。

类似于 bobD 对字体加载问题的回答,这可以通过使背景图像成为全局变量而不是每次都加载来解决。

编辑:看起来在使用 MGraph() 时也存在非常小的内存泄漏。

特别是函数 Add()。也许它还会在每次递归调用时加载一个字体库或类似的东西。

【讨论】:

    猜你喜欢
    • 2013-09-29
    • 1970-01-01
    • 2012-07-04
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多