Question

My application needs to generate a pdf file with a bunch of graphs in it.

The situation: I'm using FPDF for pdf generation and JpGraph for the graphs. My code gets the graph data from a database and iterates through, calling a function for each graph that contains all the JpGraph code for setting up, styling the graph and caching it as a png file in a cache folder on the server. FPDF then places these images in the pdf, which is served to browser.

The problem: I'm getting PHP out of memory errors when the number of graphs exceeds a certain number. AFAICT this is not an FPDF problem: in trying to diagnose the problem I have generated much larger documents with many more (pre-generated) graphs and images of equivalent size. The problem seems to be that the memory used to render the graph in the graph rendering function is not freed when the function is completed. This is based on the fact that if I call memory_get_peak_usage in the function I get a bunch of increasing numbers, one from each time the function is called, up to the limit of 64MB where it stops.

My graph generation script looks something like this:

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>";
}

As you can see, I've tried unsetting and nullifying the graph and bplot objects, although my understanding is that this shouldn't be necessary. Shouldn't all the memory used by the Graph and Bplot instances be freed up when the function finishes? Or is this perhaps a JpGraph memory leak? (I've searched high and low and can't find anyone else complaining about this). This is my first remotely resource-heavy PHP project, so I could be missing something obvious.

Was it helpful?

Solution

I had the same problem and found the solution after just an hour or so.

The issue is that jpgraph loads a default set of font files each time a Graph is created. I couldn't find a way to unload a font, so I made a slight change so that it only loads the fonts one time.

To make the fix for your installation, edit "gd_image.inc.php" as follows:

Add the following somewhere near the beginning of the file (just before the CLASS Image):

// 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"));

then at the end of the Image class constructor (lines 91-95), replace this:

$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");

with this:

$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;

I didn't test this with multiple versions of php or jpgraph, but it should work fine. ymmv.

OTHER TIPS

You could try using PHP >= 5.3 Garbage collection

gc_enable() + gc_collect_cycles()

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

@bobD's answer is right on the money and helped solved my same question.

However there is also one other potential memory leak source for those still looking for an answer to this very old problem.

If you are creating multiple charts with the same background image, each load of the background image causes an increase in memory with each chart creation.

Similar to bobD's answer to the font loading issue, this can be solved by making the background image(s) global variables instead of loading them each time.

EDIT: It looks like there is a very small memory leak when using MGraph() as well.

Specifically the function Add(). Perhaps it also loads a font library or something similar with each recursive call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top