phpout-of-memoryjpgraph

PHP / JpGraph - Memory not freed on completion of function


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.


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.