Bug #69305 [NEW]: memory leak in json_encode()
| From: | zoopi01 at gmail dot com | Date: | Thu, 26 Mar 2015 11:16:59 +0000 |
| Subject: | Bug #69305 [NEW]: memory leak in json_encode() | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-191614@lists.php.net to get a copy of this message | ||
From: zoopi01 at gmail dot com
Operating system: CentOS 5.7
PHP version: 5.4.39
Package: JSON related
Bug Type: Bug
Bug description:memory leak in json_encode()
Description:
------------
A few days ago, my server ran out of memory and hung up.
The server is on CentOS 5.7, Apache httpd(prefork) 2.2.22 and PHP
5.4.39.
Please see below test code for reproducing the similar situation.
After converted the json object into a json string for returning a
result,
and memory usage of a httpd process was increased over than 160m and
remained.
(The value of memory_limit in php.ini is 32m.)
For investigating this problem, I checked pmap and smaps output.
---------------------------------------------
# pmap -x <pid>
...
00002b6c4d374000 9364 8496 8496 rw--- [ anon ]
00002b6c4de76000 160236 154692 154692 rw--- [ anon ]
00007fff5f4b4000 84 44 44 rw--- [ stack ]
...
# cat /proc/<pid>/smaps
...
2b6c4de76000-2b6c57af1000 rw-p 2b6c4de76000 00:00 0
Size: 160236 kB
Rss: 154692 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 154692 kB
Swap: 0 kB
Pss: 154692 kB
...
---------------------------------------------
And I traced system calls through strace. Many anonymous pages were
allocated by mmap() syscall,
and munmap() syscall corresponding mmap() weren't called.
(I reproduced the situation for debugging, and some addresses are
different as before.)
---------------------------------------------
# strace -p <pid> | egrep "mmap|mremap|munmap"
...
mmap(NULL, 11276288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0) = 0x2b1307754000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13087f7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13088f7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13089f7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308af7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308bf7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308cf7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308df7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308ef7000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1308ff7000
munmap(0x2b1307754000, 11276288) = 0
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307754000
mmap(NULL, 1183744, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307854000
mmap(NULL, 1445888, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307975000
mmap(NULL, 1708032, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307ad6000
mmap(NULL, 1970176, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307c77000
mmap(NULL, 2232320, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1307e58000
mmap(NULL, 2494464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13090f7000
mmap(NULL, 2756608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1309358000
mmap(NULL, 3018752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13095f9000
mmap(NULL, 3280896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b13098da000
mmap(NULL, 3543040, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1309bfb000
mmap(NULL, 3805184, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b1309f5c000
mmap(NULL, 4067328, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130a2fd000
mmap(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130a6de000
mmap(NULL, 4591616, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130aaff000
mmap(NULL, 4853760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130af60000
mmap(NULL, 5115904, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130b401000
mmap(NULL, 5378048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130b8e2000
mmap(NULL, 5640192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130be03000
mmap(NULL, 5902336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130c364000
mmap(NULL, 6164480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130c905000
mmap(NULL, 6426624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x2b130cee6000
...
---------------------------------------------
So, I looked into that who allocated that many pages.
(I reproduced the situation for debugging, and some addresses are
different as before.)
---------------------------------------------
...
Catchpoint 1 (call to syscall 'mmap'), 0x00002b8e14b8b0ea in mmap64 ()
from /lib64/libc.so.6
(gdb) bt
#0 0x00002b8e14b8b0ea in mmap64 () from /lib64/libc.so.6
#1 0x00002b8e14b2d9b6 in _int_malloc () from /lib64/libc.so.6
#2 0x00002b8e14b2e6df in _int_realloc () from /lib64/libc.so.6
#3 0x00002b8e14b2f3e2 in realloc () from /lib64/libc.so.6
#4 0x00002b8e1d75ab50 in zend_mm_mem_malloc_realloc
(storage=0x2b8e1a762300, ptr=0x2b8e25399030, size=524288) at
/root/php-5.4.39/Zend/zend_alloc.c:292
#5 0x00002b8e1d75efd9 in _zend_mm_realloc_int (heap=0x2b8e1a762320,
p=0x2b8e25399090, size=261984, __zend_filename=0x2b8e1d8b5428
"/root/php-5.4.39/ext/json/json.c", __zend_lineno=530,
__zend_orig_filename=0x0, __zend_orig_lineno
at /root/php-5.4.39/Zend/zend_alloc.c:2292
#6 0x00002b8e1d75f7d5 in _erealloc (ptr=0x2b8e25399090, size=261984,
allow_failure=0, __zend_filename=0x2b8e1d8b5428
"/root/php-5.4.39/ext/json/json.c", __zend_lineno=530,
__zend_orig_filename=0x0, __zend_orig_lineno=0)
at /root/php-5.4.39/Zend/zend_alloc.c:2446
#7 0x00002b8e1d5e65d5 in json_escape_string (buf=0x7fff42111ca0,
s=0x2b8e1f9c7610
"abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcdefg123456íê¸ì
ëë¤abcd"...,
len=1800,
options=0) at /root/php-5.4.39/ext/json/json.c:530
#8 0x00002b8e1d5e25ba in json_encode_array (buf=0x7fff42111ca0,
val=0x7fff421114a0, options=0) at /root/php-5.4.39/ext/json/json.c:299
#9 0x00002b8e1d5e8594 in php_json_encode (buf=0x7fff42111ca0,
val=0x2b8e1f9c26e0, options=0) at /root/php-5.4.39/ext/json/json.c:642
#10 0x00002b8e1d5e238f in json_encode_array (buf=0x7fff42111ca0,
val=0x7fff42111820, options=0) at /root/php-5.4.39/ext/json/json.c:279
#11 0x00002b8e1d5e8594 in php_json_encode (buf=0x7fff42111ca0,
val=0x2b8e1f497b60, options=0) at /root/php-5.4.39/ext/json/json.c:642
#12 0x00002b8e1d5e275d in json_encode_array (buf=0x7fff42111ca0,
val=0x7fff42111ba0, options=0) at /root/php-5.4.39/ext/json/json.c:304
#13 0x00002b8e1d5e8594 in php_json_encode (buf=0x7fff42111ca0,
val=0x2b8e1f493ed8, options=0) at /root/php-5.4.39/ext/json/json.c:642
#14 0x00002b8e1d5e8d15 in zif_json_encode (ht=1,
return_value=0x2b8e1f492978, return_value_ptr=0x0, this_ptr=0x0,
return_value_used=1) at /root/php-5.4.39/ext/json/json.c:778
...
---------------------------------------------
json_encode() called so many mmap() syscalls and didn't call any
munmap() syscalls.
Is this a memory leak of json_encode()? Or any other purpose like memory
reuse?
Test script:
---------------
<?php
// some large data from remote server consisting of utf8 unicde char.
// (It's a just example.)
$text = str_repeat("abcdefg123456íê¸ì
ëë¤", 100);
$data = "{\"result\":[" . str_repeat("{\"" . $text .
"\":\"" . $text .
"\"},", 1000);
$data = rtrim($data, ",");
$data .= "]}";
// make a json object
$json = json_decode($data);
// manipulating the json object
// return a result
echo json_encode($json);
?>
--
Edit bug report at https://bugs.php.net/bug.php?id=69305&edit=1
--
Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=69305&r=trysnapshot54
Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=69305&r=trysnapshot55
Try a snapshot (trunk): https://bugs.php.net/fix.php?id=69305&r=trysnapshottrunk
Fixed in SVN: https://bugs.php.net/fix.php?id=69305&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=69305&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=69305&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=69305&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=69305&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=69305&r=support
Expected behavior: https://bugs.php.net/fix.php?id=69305&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=69305&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=69305&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=69305&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=69305&r=php4
Daylight Savings: https://bugs.php.net/fix.php?id=69305&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=69305&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=69305&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=69305&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=69305&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=69305&r=mysqlcfg