php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79518 SimpleXML memory leak
Submitted: 2020-04-24 10:35 UTC Modified: -
From: ivo at beerntea dot com Assigned:
Status: Open Package: SimpleXML related
PHP Version: 7.3.17 OS: Linux
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: ivo at beerntea dot com
New email:
PHP Version: OS:

 

 [2020-04-24 10:35 UTC] ivo at beerntea dot com
Description:
------------
SimpleXML and DOMDocument (and possibly other libxml functions) do not always free the allocated memory after all references to the XML/DOM object have been released.

This especially is a problem in environments where the same process is used to handle multiple requests, like the FPM interface, as memory allocated by one script won't be released until the FPM process is terminated. When handling large XML files this can easily push a server out of memory.

In addition the XML DOM appeard to use significantly more memory than the original XML string, and the memory used by the XML DOM is not accounted for by PHP (see #63380), allowing to bypass the PHP memory limit.

PHP version: 7.3.17-1+ubuntu18.04.1+deb.sury.org+1
Linux kernel 4.15.0-91-generic x86_64

Test script:
---------------
<?php
function mstat() {
  //Print reserved memory according to PHP and total mapped memory according to OS (assumes 4k page size and Linux) 
  $statm = explode(' ', file_get_contents('/proc/self/statm'));
  printf("PHP memory usage=%u MB, process memory usage=%u MB\n", memory_get_usage(TRUE) / 1024 / 1024, $statm[0] * 4096 / 1024 / 1024);
}

mstat();

$xml = '<doc>'.str_repeat('<node attr="attr">test</node>', 1000000).'</doc>';
printf("Preparing big XML: %u MB.\n", strlen($xml) / 1024 / 1024);
mstat();

$flags = 0;
$flags |= LIBXML_COMPACT; //COMPACT flag reduces memory usage a little bit, not much

echo "Parsing DOM document without storing reference...\n";
(new \DOMDocument())->loadXML($xml, $flags);
mstat();

echo "Parse XML DOM without storing reference...\n";
simplexml_load_string($xml, 'SimpleXMLElement', $flags);  
mstat();

echo "Parse XML DOM without storing reference...\n";
simplexml_load_string($xml, 'SimpleXMLElement', $flags);  
mstat();

echo "Parse XML DOM storing reference...\n";
$dom = simplexml_load_string($xml, 'SimpleXMLElement', $flags);  
mstat();

echo "Parse XML DOM overwriting reference...\n";
$dom = simplexml_load_string($xml, 'SimpleXMLElement', $flags);  
mstat();

echo "Unset XML string...\n";
$xml = NULL;
mstat();

echo "Unset DOM references...\n";
$dom = $dom2 = $dom3 = NULL;
mstat();

Expected result:
----------------
Indicated memory usage should return back to the initial value after a reference to the XML/DOM object is cleared.

Actual result:
--------------
The memory used by the XML functions remains in use even when there are no more references:

PHP memory usage=2 MB, process memory usage=391 MB
Preparing big XML: 27 MB.
PHP memory usage=29 MB, process memory usage=419 MB
Parsing DOM document without storing reference...
PHP memory usage=29 MB, process memory usage=892 MB


Patches

Add a Patch

Pull Requests

Add a Pull Request

 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Wed Jul 08 02:01:27 2020 UTC