Php: Tcpdf not freeing memory because of register shutdown function

From FVue
Jump to: navigation, search

Problem

When generating multiple PDF's with repeated invocations of TCPDF, PHP will finally generate a error:

Fatal error: Allowed memory size of 209715200 bytes exhausted
(tried to allocate 799680 bytes) in tcpdf.php on line 7284

Environment

  • php-5.6.31
  • tcpdf-6.2.12

Workaround

Manually call the destructor after having generated the PDF:

$pdf->__destruct();

Rationale

It looks like the register_shutdown_function() of the TCPDF object causes the TCPDF object not to be destroyed by the garbage collector, as can be seen by running this script which creates a 1000 TCPDF objects repeatedly:

for ($i = 1; $i <= 1000; $i++) {
    $pdf = new TCPDF();
    printf("% 4d %s\n", $i, memory_get_usage(true));
}
 
Output:
   1 13107200
   2 13631488
   : :
 998 126353408
 999 126353408
1000 126353408

Output shows, memory usage will grow until 120M by just creating a 1000 TCPDF objects.

We can also trigger the Fatal error "Allowed memory size exhausted" by setting the memory limit to 100 Megabytes:

ini_set("memory_limit","100M");
for ($i = 1; $i <= 1000; $i++) {
    $pdf = new TCPDF();
    printf("% 4d %s\n", $i, memory_get_usage(true));
}
 
Output:
   1 13107200
   2 13631488
   : :
 812 104857600
PHP Fatal error:  Allowed memory size of 104857600 bytes exhausted
 (tried to allocate 49152 bytes) in ./tcpdf/fonts/helvetica.php on line 11

If we disable the register_shutdown_function() in TCPDF::__construct():

#register_shutdown_function(array($this, '_destroy'), true);

the memory consumption doesn't grow:

   1 13107200
   2 13631488
   : :
1000 13631488

If we leave the register_shutdown_function() in TCPDF::__construct() intact:

register_shutdown_function(array($this, '_destroy'), true);

and manually call TCPDF::_destruct(), the memory use will grow, albeit at a much slower rate, from 13M to 20M:

for ($i = 1; $i <= 1000; $i++) {
    $pdf = new TCPDF();
    $pdf->__destruct();
    printf("% 4d %s\n", $i, memory_get_usage(true));
}
 
Output:
 
   1 13107200
   2 13107200
   : :
1000 19660800

This is probably due to the call to register_shutdown_function() which keeps references to ($this, '_destroy') for each $pdf object.