krakjoe / pthreads

Threading for PHP - Share Nothing, Do Everything :)
Other
3.47k stars 501 forks source link

Memory leak for daemons #524

Closed degtyaryov closed 8 years ago

degtyaryov commented 8 years ago

Hello.

I did the same examples in php and C.

PHP example:

<?php

class ThreadTest extends Thread {
        public function run() {
                sleep(2);
        }

        public function __destruct() {
                echo '#';
        }
}

$threads_count = 1000;
$threads = [];
for ($i = 0; $i < $threads_count; $i++) {
        $thread = new ThreadTest();
        if (!$thread->start()) {
                die('ERROR, so far '.$i.' threads created'."\n");
        }
        $threads[] = $thread;
}

foreach ($threads as $i => $thread) {
        if (!$thread->join()) {
                echo 'ERROR, joined with thread ',$i,"\n";
        }
}

unset($i, $threads, $thread, $threads_count);

while (true) {
        echo '.';
        sleep(1);
}
?>

C example:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

static void * run(void * arg) {
        sleep(2);
        printf("##");
        pthread_exit(0);
}

int main () {
        int threads_count = 1000;
        pthread_t threads[threads_count];
        pthread_t thread;
        int rc, i;
        for (i = 0; i < threads_count; i++) {
                if (rc = pthread_create(&thread, 0, &run, 0)) {
                        printf("ERROR, rc is %d, so far %ld threads created\n", rc, i);
                        perror("Fail:");
                        return -1;
                }
                threads[i] = thread;
        }

        void *res;
        for (i = 0; i < threads_count; i++) {
                rc = pthread_join(threads[i], &res);
                if (rc != 0) {
                        printf("ERROR, joined with thread %d; returned value was %s\n", i, (char *) res);
                }
        }

        setbuf(stdout, NULL);
        while (1) {
                printf(".");
                sleep(1);
        }
        return 0;
}

Result PHP:

################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################................................................................................................................................................................

Result C:

################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################................................................................................................................................................................

In PHP memory is not freed! For PHP7:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
 5286 test      20   0 4455648 423792  13168 S   0,0  5,2   0:02.44 zts-php test.php                                                                                                                                                                                          

For PHP5.5:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
20231 test      20   0 2708876 1,793g  10780 S   0,0 31,2   0:03.48 zts-php test.php

For C:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
 6308 test      20   0  107188   1984   1556 S   0,0  0,0   0:00.07 ./a.out

If you remove the sleep(2) in threads then memory freed: For PHP7:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
 8445 test      20   0  451384  23216  13220 S   0,0  0,3   0:02.68 zts-php test.php                                                                                                                                                                                          

For PHP5.5:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
18507 test      20   0  618624  27960  10756 S   0,0  0,5   0:02.81 zts-php test.php

For C:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
 9473 test      20   0  107188   1908   1476 S   0,0  0,0   0:00.06 ./a.out

Tested on versions 2.0.11 and 3.1.3

php-pecl-pthreads-2.0.11-1.fc19.5.5.x86_64
php-pecl-pthreads-3.1.3-1.fc23.7.0.x86_64

This is a real problem for PHP demons!

BurakDev commented 8 years ago

Maybe related to #497 @krakjoe said "Don't use anything not designed for multi-threading."

krakjoe commented 8 years ago

There is no bug here.

PHP and C manage memory differently.

degtyaryov commented 8 years ago

I thought that the problem is pthread_join, but example C gave me to understand that is not the point.

If sleep is replaced by any code then problem is so exists:

<?php

class ThreadTest extends Thread {
        public function run() {
                for ($i = 0; $i < 2500000; $i++) {
                }
        }

        public function __destruct() {
                echo '#';
        }
}

$threads_count = 1000;
$threads = [];
for ($i = 0; $i < $threads_count; $i++) {
        $thread = new ThreadTest();
        if (!$thread->start()) {
                die('ERROR, so far '.$i.' threads created'."\n");
        }
        $threads[] = $thread;
}

foreach ($threads as $i => $thread) {
        if (!$thread->join()) {
                echo 'ERROR, joined with thread ',$i,"\n";
        }
}

unset($i, $threads, $thread, $threads_count);

while (true) {
        echo '.';
        sleep(1);
}
?>
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   
 3912 test      20   0 2590204 379308  10768 S   0,0  6,3   4:01.45 zts-php test.php

Why process using 400Mb memory?

BurakDev commented 8 years ago

@degtyaryov On Windows 10 x64 it just crash, 2 Gb RAM used and Windows say "CLI stopped working"

degtyaryov commented 8 years ago

My OS Fedora 23, RAM 6 Gb.

Change thread count for test on machine 2Gb RAM:

$threads_count = 500;

or more less

$threads_count = 300;

In moment working while(true) { sleep(1); } memory must be released. Why 400 Mb?

BurakDev commented 8 years ago

Test this function http://php.net/manual/fr/function.gc-collect-cycles.php on your while

degtyaryov commented 8 years ago

gc_enable and gc_collect_cycles not solve the problem.

I change test to:

<?php

echo 'run:            ', date('Y-m-d H:i:s'), ' - memory_get_usage(): ', memory_get_usage(), ', memory_get_usage(true): ', memory_get_usage(true), "\n";
gc_enable();

class ThreadTest extends Thread {
        public function run() {
                gc_enable();
                //for ($i = 0; $i < 2500000; $i++) {} // For PHP5, pthreads-2.0.11, Fedora 19, CPU 4 Cores, RAM 6GB
                for ($i = 0; $i < 30000000; $i++) {}  // For PHP7, pthreads-3.1.3, Fedora 23, CPU 8 Cores, RAM 8GB
                gc_collect_cycles();
        }
}

$threads_count = 1000;
$threads = [];
for ($i = 0; $i < $threads_count; $i++) {
        $thread = new ThreadTest();
        if (!$thread->start()) {
                die('ERROR, so far '.$i.' threads created'."\n");
        }
        $threads[] = $thread;
}

foreach ($threads as $i => $thread) {
        if (!$thread->join()) {
                echo 'ERROR, joined with thread ',$i,"\n";
        }
}

echo 'before unset:   ', date('Y-m-d H:i:s'), ' - memory_get_usage(): ', memory_get_usage(), ', memory_get_usage(true): ', memory_get_usage(true), "\n";
gc_collect_cycles();
unset($i, $threads, $thread, $threads_count);
gc_collect_cycles();
echo 'after unset:    ', date('Y-m-d H:i:s'), ' - memory_get_usage(): ', memory_get_usage(), ', memory_get_usage(true): ', memory_get_usage(true), "\n";

while (true) {
        echo 'in while loop:  ', date('Y-m-d H:i:s'), ' - memory_get_usage(): ', memory_get_usage(), ', memory_get_usage(true): ', memory_get_usage(true), "\n";
        sleep(1);
        gc_collect_cycles();
}

For PHP7, pthreads-3.1.3, Fedora 23, CPU 8 Cores, RAM 8GB:

$ zts-php ./test.php
run:            2015-11-26 14:20:18 - memory_get_usage(): 360712, memory_get_usage(true): 2097152
before unset:   2015-11-26 14:21:25 - memory_get_usage(): 526080, memory_get_usage(true): 2097152
after unset:    2015-11-26 14:21:25 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:21:25 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:21:26 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:21:27 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
......
in while loop:  2015-11-26 14:46:32 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:46:33 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:46:34 - memory_get_usage(): 361160, memory_get_usage(true): 2097152
in while loop:  2015-11-26 14:46:35 - memory_get_usage(): 361160, memory_get_usage(true): 2097152

Memory usage from ps command:

$ while true; do date -u --rfc-3339=seconds | tr '\n' ' '  && ps aux | grep 'test.php' | grep -v grep | awk '{print "VIRT: "$5", RES: "$6}'; sleep 1; done
2015-11-26 14:20:19+00:00 VIRT: 4328252, RES: 30824
2015-11-26 14:20:20+00:00 VIRT: 5343092, RES: 39792
2015-11-26 14:20:21+00:00 VIRT: 5619700, RES: 46712
2015-11-26 14:20:22+00:00 VIRT: 5817076, RES: 52288
2015-11-26 14:20:23+00:00 VIRT: 5949460, RES: 57572
2015-11-26 14:20:24+00:00 VIRT: 6097604, RES: 63948
2015-11-26 14:20:25+00:00 VIRT: 6210288, RES: 67052
2015-11-26 14:20:26+00:00 VIRT: 6332428, RES: 69268
2015-11-26 14:20:27+00:00 VIRT: 6449056, RES: 74132
2015-11-26 14:20:28+00:00 VIRT: 6564892, RES: 76960
2015-11-26 14:20:29+00:00 VIRT: 6688608, RES: 81216
2015-11-26 14:20:30+00:00 VIRT: 6801764, RES: 88200
2015-11-26 14:20:31+00:00 VIRT: 6913712, RES: 91640
2015-11-26 14:20:32+00:00 VIRT: 7034604, RES: 94468
2015-11-26 14:20:33+00:00 VIRT: 7178032, RES: 97028
2015-11-26 14:20:34+00:00 VIRT: 7309168, RES: 98732
2015-11-26 14:20:35+00:00 VIRT: 7420140, RES: 99916
2015-11-26 14:20:36+00:00 VIRT: 7551272, RES: 101516
2015-11-26 14:20:37+00:00 VIRT: 7676264, RES: 102428
2015-11-26 14:20:38+00:00 VIRT: 7813544, RES: 103748
2015-11-26 14:20:40+00:00 VIRT: 7928292, RES: 106564
2015-11-26 14:20:41+00:00 VIRT: 8063524, RES: 107648
2015-11-26 14:20:42+00:00 VIRT: 8178272, RES: 108560
2015-11-26 14:20:43+00:00 VIRT: 8325796, RES: 109448
2015-11-26 14:20:44+00:00 VIRT: 8442592, RES: 110332
2015-11-26 14:20:45+00:00 VIRT: 8606508, RES: 111276
2015-11-26 14:20:46+00:00 VIRT: 8772472, RES: 111948
2015-11-26 14:20:47+00:00 VIRT: 8883120, RES: 112836
2015-11-26 14:20:48+00:00 VIRT: 9036792, RES: 113040
2015-11-26 14:20:49+00:00 VIRT: 9165876, RES: 114120
2015-11-26 14:20:50+00:00 VIRT: 9301108, RES: 114468
2015-11-26 14:20:51+00:00 VIRT: 9409708, RES: 114868
2015-11-26 14:20:52+00:00 VIRT: 9524452, RES: 115412
2015-11-26 14:20:53+00:00 VIRT: 9612560, RES: 115936
2015-11-26 14:20:54+00:00 VIRT: 9725260, RES: 116764
2015-11-26 14:20:55+00:00 VIRT: 9840008, RES: 116992
2015-11-26 14:20:56+00:00 VIRT: 9971144, RES: 117448
2015-11-26 14:20:57+00:00 VIRT: 10120720, RES: 117860
2015-11-26 14:20:58+00:00 VIRT: 10253908, RES: 118456
2015-11-26 14:20:59+00:00 VIRT: 10378896, RES: 118672
2015-11-26 14:21:00+00:00 VIRT: 10491596, RES: 119176
2015-11-26 14:21:01+00:00 VIRT: 10655508, RES: 119256
2015-11-26 14:21:02+00:00 VIRT: 10798932, RES: 119600
2015-11-26 14:21:03+00:00 VIRT: 10915728, RES: 119744
2015-11-26 14:21:04+00:00 VIRT: 11032840, RES: 120184
2015-11-26 14:21:06+00:00 VIRT: 11174216, RES: 120376
2015-11-26 14:21:07+00:00 VIRT: 11270528, RES: 120884
2015-11-26 14:21:08+00:00 VIRT: 11391420, RES: 121076
2015-11-26 14:21:09+00:00 VIRT: 11516408, RES: 121472
2015-11-26 14:21:10+00:00 VIRT: 11643448, RES: 121684
2015-11-26 14:21:11+00:00 VIRT: 11807364, RES: 122284
2015-11-26 14:21:12+00:00 VIRT: 11915964, RES: 122828
2015-11-26 14:21:13+00:00 VIRT: 12036856, RES: 123384
2015-11-26 14:21:14+00:00 VIRT: 12200768, RES: 123500
2015-11-26 14:21:15+00:00 VIRT: 12309372, RES: 123944
2015-11-26 14:21:16+00:00 VIRT: 12456896, RES: 124048
2015-11-26 14:21:17+00:00 VIRT: 12585980, RES: 124520
2015-11-26 14:21:18+00:00 VIRT: 12698676, RES: 125024
2015-11-26 14:21:19+00:00 VIRT: 12821616, RES: 125540
2015-11-26 14:21:20+00:00 VIRT: 5883708, RES: 112068
2015-11-26 14:21:21+00:00 VIRT: 5701364, RES: 111968
2015-11-26 14:21:22+00:00 VIRT: 5475996, RES: 111904
2015-11-26 14:21:23+00:00 VIRT: 5260872, RES: 111736
2015-11-26 14:21:24+00:00 VIRT: 4826528, RES: 111428
2015-11-26 14:21:25+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:21:26+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:21:27+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:21:28+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:21:29+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:21:30+00:00 VIRT: 4451596, RES: 111116
......
2015-11-26 14:46:27+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:28+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:29+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:30+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:31+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:32+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:33+00:00 VIRT: 4451596, RES: 111116
2015-11-26 14:46:34+00:00 VIRT: 4451596, RES: 111116

For PHP5, pthreads-2.0.11, Fedora 19, CPU 4 Cores, RAM 6GB:

$ zts-php ./test.php
run:            2015-11-26 19:32:06 - memory_get_usage(): 251568, memory_get_usage(true): 262144
before unset:   2015-11-26 19:33:08 - memory_get_usage(): 396752, memory_get_usage(true): 524288
after unset:    2015-11-26 19:33:08 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:33:08 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:33:09 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:33:10 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:33:11 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:33:12 - memory_get_usage(): 251872, memory_get_usage(true): 524288
.........
in while loop:  2015-11-26 19:54:02 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:54:03 - memory_get_usage(): 251872, memory_get_usage(true): 524288
in while loop:  2015-11-26 19:54:04 - memory_get_usage(): 251872, memory_get_usage(true): 524288

Memory usage from ps command:

2015-11-26 14:32:07+00:00 VIRT: 2533564, RES: 78708
2015-11-26 14:32:08+00:00 VIRT: 2996164, RES: 142212
2015-11-26 14:32:09+00:00 VIRT: 3166640, RES: 173244
2015-11-26 14:32:10+00:00 VIRT: 3322496, RES: 191480
2015-11-26 14:32:11+00:00 VIRT: 3471372, RES: 210864
2015-11-26 14:32:12+00:00 VIRT: 3602064, RES: 221764
2015-11-26 14:32:13+00:00 VIRT: 3736788, RES: 229784
2015-11-26 14:32:14+00:00 VIRT: 3884316, RES: 237888
2015-11-26 14:32:15+00:00 VIRT: 4015452, RES: 242652
2015-11-26 14:32:16+00:00 VIRT: 4146720, RES: 249452
2015-11-26 14:32:17+00:00 VIRT: 4294248, RES: 261512
2015-11-26 14:32:18+00:00 VIRT: 4433580, RES: 266764
2015-11-26 14:32:19+00:00 VIRT: 4572912, RES: 267816
2015-11-26 14:32:20+00:00 VIRT: 4728636, RES: 271104
2015-11-26 14:32:21+00:00 VIRT: 4859772, RES: 278180
2015-11-26 14:32:22+00:00 VIRT: 4999236, RES: 280840
2015-11-26 14:32:23+00:00 VIRT: 5138568, RES: 284760
2015-11-26 14:32:24+00:00 VIRT: 5269704, RES: 285248
2015-11-26 14:32:25+00:00 VIRT: 5400840, RES: 286124
2015-11-26 14:32:26+00:00 VIRT: 5548368, RES: 286676
2015-11-26 14:32:27+00:00 VIRT: 5687832, RES: 289552
2015-11-26 14:32:28+00:00 VIRT: 5818968, RES: 290108
2015-11-26 14:32:30+00:00 VIRT: 5950104, RES: 290708
2015-11-26 14:32:31+00:00 VIRT: 6081240, RES: 291072
2015-11-26 14:32:32+00:00 VIRT: 6220572, RES: 293588
2015-11-26 14:32:33+00:00 VIRT: 6360036, RES: 293972
2015-11-26 14:32:34+00:00 VIRT: 6491172, RES: 294412
2015-11-26 14:32:35+00:00 VIRT: 6630504, RES: 295700
2015-11-26 14:32:36+00:00 VIRT: 6769836, RES: 299256
2015-11-26 14:32:37+00:00 VIRT: 6900972, RES: 303972
2015-11-26 14:32:38+00:00 VIRT: 7040436, RES: 306568
2015-11-26 14:32:39+00:00 VIRT: 7171572, RES: 307180
2015-11-26 14:32:40+00:00 VIRT: 7310904, RES: 309444
2015-11-26 14:32:41+00:00 VIRT: 7450236, RES: 310036
2015-11-26 14:32:42+00:00 VIRT: 7605960, RES: 310600
2015-11-26 14:32:43+00:00 VIRT: 7728900, RES: 313708
2015-11-26 14:32:44+00:00 VIRT: 7868364, RES: 316192
2015-11-26 14:32:45+00:00 VIRT: 8015892, RES: 316644
2015-11-26 14:32:46+00:00 VIRT: 8130636, RES: 319112
2015-11-26 14:32:47+00:00 VIRT: 8269968, RES: 319520
2015-11-26 14:32:48+00:00 VIRT: 8409300, RES: 319868
2015-11-26 14:32:49+00:00 VIRT: 8548632, RES: 323136
2015-11-26 14:32:50+00:00 VIRT: 8704356, RES: 323248
2015-11-26 14:32:51+00:00 VIRT: 8835492, RES: 330160
2015-11-26 14:32:52+00:00 VIRT: 8958432, RES: 330292
2015-11-26 14:32:53+00:00 VIRT: 9114156, RES: 330732
2015-11-26 14:32:54+00:00 VIRT: 9245292, RES: 337796
2015-11-26 14:32:55+00:00 VIRT: 9376428, RES: 346820
2015-11-26 14:32:56+00:00 VIRT: 9515760, RES: 349076
2015-11-26 14:32:57+00:00 VIRT: 9655092, RES: 354584
2015-11-26 14:32:58+00:00 VIRT: 9786228, RES: 354804
2015-11-26 14:32:59+00:00 VIRT: 9909168, RES: 358004
2015-11-26 14:33:00+00:00 VIRT: 10048500, RES: 359564
2015-11-26 14:33:01+00:00 VIRT: 10163244, RES: 367144
2015-11-26 14:33:02+00:00 VIRT: 10302576, RES: 367820
2015-11-26 14:33:03+00:00 VIRT: 10441908, RES: 368664
2015-11-26 14:33:04+00:00 VIRT: 10589436, RES: 368868
2015-11-26 14:33:05+00:00 VIRT: 3229428, RES: 358896
2015-11-26 14:33:06+00:00 VIRT: 3098292, RES: 359452
2015-11-26 14:33:08+00:00 VIRT: 2852412, RES: 359448
2015-11-26 14:33:09+00:00 VIRT: 2524572, RES: 359660
2015-11-26 14:33:10+00:00 VIRT: 2524572, RES: 359660
2015-11-26 14:33:11+00:00 VIRT: 2524572, RES: 359660
........
2015-11-26 14:54:01+00:00 VIRT: 2524572, RES: 359660
2015-11-26 14:54:02+00:00 VIRT: 2524572, RES: 359660
2015-11-26 14:54:03+00:00 VIRT: 2524572, RES: 359660
2015-11-26 14:54:04+00:00 VIRT: 2524572, RES: 359660

There is a problem only when the long run many parallel threads!

This test on C language max memory usage 9Kb, min 1 Kb.

In PHP $threads array only using 162Kb memory. Why the process using 109Mb for test PHP7 and 352Mb for test PHP5?

krakjoe commented 8 years ago

You are creating 1000 instances of the interpreter. You can expect 1000 threads to use about as much memory as 1000 processes executing the same code, actually a little more.

I think you are expecting them to share address space and so you are not expecting more threads to mean more memory, well it does for PHP. PHP is shared nothing and pthreads can't break that.

C and PHP manage memory very differently, you are comparing apples and oranges there.

You should not create threads and destroy them, only to recreate them later on in the process, you should reuse them. This is what Worker and Pool are aimed at.

1000 threads isn't a sensible number of threads to create on any hardware, these are kernel threads, and it'll be a long time before we can access processors capable of executing 1000 threads concurrently.

Design your infrastructure sensibly and this stuff isn't a problem.

degtyaryov commented 8 years ago

Now I do not care about performance issues. My thread can use the CPU, or disk, or network, or all at once. This is only a test.

I am concerned about the stability and adequacy of the behavior of the PHP. This test shows that the PHP with pthreads behaves unstable with respect to memory. Exists memory leak.

I think, normal behavior is for the pool bad(only stable, not performance):

  1. Thread::start() return false value or "PHP Fatal error: Uncaught RuntimeException: cannot start ThreadTest, out of resources". i.e. system call pthread_create return error EAGAIN(http://man7.org/linux/man-pages/man3/pthread_create.3.html).
  2. PHP Fatal error: Allowed memory size...
  3. zts-php invoked oom-killer. CPU: 0 PID: 29066 Comm: zts-php Tainted: G.

My test makes the following:

  1. main process has started;
  2. main process start 1000 threads;
  3. main process wait shutdown 1000 threads;
  4. all 1000 threads successful shutdowned;
  5. main process unset all variables absolutely;
  6. main process continues to run.

In the state 1 and 6 memory usage should be the same! The test work properly and nothing is violated.

For memory_get_usage, in state 1 using 353 Kb and in state 6 is also using 353 Kb. This is Good!

But for PS linux command, in state 1 using 30 Mb and in state 6 using 108 Mb. This is not normal :-(. In the state 6 should be also 30Mb.

What kind of data is occupied by the memory?

krakjoe commented 8 years ago

Not all memory allocated is included in the value returned by `memory_get_usage``, only memory allocated by Zend's mm, and in addition, only memory allocated for the instance of the interpreter that called the function (each instance has it's own heap).

This doesn't include a lot of the memory allocated for modules globals and other stuff required for the interpreter to work.

It is very normal for the system and memory_get_usage to disagree about memory usage, very normal.

Whenever you start a new thread, the interpreter must be initialized for the new thread, but may not have all resources that were allocated free'd until the whole process is shutdown.

degtyaryov commented 8 years ago

It is very normal for the system and memory_get_usage to disagree about memory usage, very normal.

I agree

but may not have all resources that were allocated free'd until the whole process is shutdown.

This not normal. This is mistake. This is a memory leak. All resources must be freed!

I using valgrind for detect memory leak:

$ export ZEND_DONT_UNLOAD_MODULES=1 && valgrind --max-threads=2000 --leak-check=full --show-leak-kinds=all --leak-resolution=high --allow-mismatched-debuginfo=yes zts-php test.php
==19368== Memcheck, a memory error detector
==19368== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19368== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==19368== Command: zts-php test.php
==19368== 
==19368== 
==19368== HEAP SUMMARY:
==19368==     in use at exit: 2,601,658 bytes in 27,499 blocks
==19368==   total heap usage: 3,598,451 allocs, 3,570,952 frees, 831,973,823 bytes allocated
==19368== 
==19368== 96 bytes in 1 blocks are possibly lost in loss record 83 of 183
==19368==    at 0x4C2A9C7: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==19368==    by 0x15F24478: UnknownInlinedFun (copy.h:287)
==19368==    by 0x15F24478: pthreads_copy_function (copy.h:306)
==19368==    by 0x15F255B1: pthreads_copy_entry (prepare.c:169)
==19368==    by 0x15F255B1: pthreads_prepared_entry (prepare.c:396)
==19368==    by 0x15F278FA: pthreads_prepare_classes (prepare.c:567)
==19368==    by 0x15F278FA: pthreads_prepared_startup (prepare.c:680)
==19368==    by 0x15F2C180: pthreads_routine (object.c:482)
==19368==    by 0x7DDD609: start_thread (pthread_create.c:334)
==19368==    by 0x80F5A7C: clone (clone.S:109)
==19368== 
==19368== 73,728 bytes in 1 blocks are possibly lost in loss record 182 of 183
==19368==    at 0x4C28C50: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==19368==    by 0x345138: __zend_malloc (zend_alloc.c:2852)
==19368==    by 0x37A75C: zend_hash_do_resize (zend_hash.c:854)
==19368==    by 0x37D41F: _zend_hash_index_add_or_update_i (zend_hash.c:783)
==19368==    by 0x37D41F: _zend_hash_index_update (zend_hash.c:825)
==19368==    by 0x15F24045: UnknownInlinedFun (zend_hash.h:609)
==19368==    by 0x15F24045: pthreads_globals_object_alloc (globals.c:82)
==19368==    by 0x15F2BE3A: pthreads_thread_ctor (object.c:138)
==19368==    by 0x37015A: _object_and_properties_init (zend_API.c:1300)
==19368==    by 0x15F2C274: pthreads_routine (object.c:486)
==19368==    by 0x7DDD609: start_thread (pthread_create.c:334)
==19368==    by 0x80F5A7C: clone (clone.S:109)
==19368== 
==19368== 2,207,904 (2,207,328 direct, 576 indirect) bytes in 22,993 blocks are definitely lost in loss record 183 of 183
==19368==    at 0x4C2A9C7: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==19368==    by 0x15F24478: UnknownInlinedFun (copy.h:287)
==19368==    by 0x15F24478: pthreads_copy_function (copy.h:306)
==19368==    by 0x15F255B1: pthreads_copy_entry (prepare.c:169)
==19368==    by 0x15F255B1: pthreads_prepared_entry (prepare.c:396)
==19368==    by 0x15F278FA: pthreads_prepare_classes (prepare.c:567)
==19368==    by 0x15F278FA: pthreads_prepared_startup (prepare.c:680)
==19368==    by 0x15F2C180: pthreads_routine (object.c:482)
==19368==    by 0x7DDD609: start_thread (pthread_create.c:334)
==19368==    by 0x80F5A7C: clone (clone.S:109)
==19368== 
==19368== LEAK SUMMARY:
==19368==    definitely lost: 2,207,328 bytes in 22,993 blocks
==19368==    indirectly lost: 576 bytes in 6 blocks
==19368==      possibly lost: 73,824 bytes in 2 blocks
==19368==    still reachable: 319,930 bytes in 4,498 blocks
==19368==         suppressed: 0 bytes in 0 blocks
==19368== Reachable blocks (those to which a pointer was found) are not shown.
==19368== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==19368== 
==19368== For counts of detected and suppressed errors, rerun with: -v
==19368== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
$ export ZEND_DONT_UNLOAD_MODULES=1 && valgrind --tool=helgrind --max-threads=2000 zts-php test.php
.....
==19870== ----------------------------------------------------------------
==19870== 
==19870== Possible data race during read of size 8 at 0x936B108 by thread #3
==19870== Locks held: none
==19870==    at 0x3814AB: UnknownInlinedFun (zend_string.h:84)
==19870==    by 0x3814AB: zend_hash_find_bucket (zend_hash.c:469)
==19870==    by 0x3814AB: zend_hash_find (zend_hash.c:1926)
==19870==    by 0x15F26234: UnknownInlinedFun (zend_hash.h:671)
==19870==    by 0x15F26234: pthreads_routine_run_function (object.c:449)
==19870==    by 0x15F32296: pthreads_routine (object.c:487)
==19870==    by 0x4C2F967: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x7DE3609: start_thread (pthread_create.c:334)
==19870==    by 0x80FBA7C: clone (clone.S:109)
==19870== 
==19870== This conflicts with a previous write of size 8 by thread #2
==19870== Locks held: none
==19870==    at 0x381500: UnknownInlinedFun (zend_string.h:85)
==19870==    by 0x381500: zend_hash_find_bucket (zend_hash.c:469)
==19870==    by 0x381500: zend_hash_find (zend_hash.c:1926)
==19870==    by 0x15F26234: UnknownInlinedFun (zend_hash.h:671)
==19870==    by 0x15F26234: pthreads_routine_run_function (object.c:449)
==19870==    by 0x15F32296: pthreads_routine (object.c:487)
==19870==    by 0x4C2F967: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x7DE3609: start_thread (pthread_create.c:334)
==19870==    by 0x80FBA7C: clone (clone.S:109)
==19870==  Address 0x936b108 is 8 bytes inside a block of size 32 alloc'd
==19870==    at 0x4C2A070: malloc (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x345138: __zend_malloc (zend_alloc.c:2852)
==19870==    by 0x15F29E63: UnknownInlinedFun (zend_string.h:121)
==19870==    by 0x15F29E63: UnknownInlinedFun (zend_string.h:157)
==19870==    by 0x15F29E63: pthreads_globals_init (globals.c:52)
==19870==    by 0x15F28841: zm_startup_pthreads (php_pthreads.c:323)
==19870==    by 0x3720C9: zend_startup_module_ex (zend_API.c:1841)
==19870==    by 0x37FC3B: zend_hash_apply (zend_hash.c:1500)
==19870==    by 0x372489: zend_startup_modules (zend_API.c:1967)
==19870==    by 0x2FD412: php_module_startup (main.c:2194)
==19870==    by 0x40C4BC: php_cli_startup (php_cli.c:423)
==19870==    by 0x1D572A: main (php_cli.c:1325)
==19870==  Block was alloc'd by thread #1
......
==19870== ----------------------------------------------------------------
==19870== 
==19870==  Lock at 0x935B328 was first observed
==19870==    at 0x4C30BA3: pthread_mutex_init (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x15F294ED: pthreads_monitor_alloc (monitor.c:42)
==19870==    by 0x15F29E3F: pthreads_globals_init (globals.c:37)
==19870==    by 0x15F28841: zm_startup_pthreads (php_pthreads.c:323)
==19870==    by 0x3720C9: zend_startup_module_ex (zend_API.c:1841)
==19870==    by 0x37FC3B: zend_hash_apply (zend_hash.c:1500)
==19870==    by 0x372489: zend_startup_modules (zend_API.c:1967)
==19870==    by 0x2FD412: php_module_startup (main.c:2194)
==19870==    by 0x40C4BC: php_cli_startup (php_cli.c:423)
==19870==    by 0x1D572A: main (php_cli.c:1325)
==19870==  Address 0x935b328 is 8 bytes inside a block of size 96 alloc'd
==19870==    at 0x4C2BDE7: calloc (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x15F294C6: pthreads_monitor_alloc (monitor.c:33)
==19870==    by 0x15F29E3F: pthreads_globals_init (globals.c:37)
==19870==    by 0x15F28841: zm_startup_pthreads (php_pthreads.c:323)
==19870==    by 0x3720C9: zend_startup_module_ex (zend_API.c:1841)
==19870==    by 0x37FC3B: zend_hash_apply (zend_hash.c:1500)
==19870==    by 0x372489: zend_startup_modules (zend_API.c:1967)
==19870==    by 0x2FD412: php_module_startup (main.c:2194)
==19870==    by 0x40C4BC: php_cli_startup (php_cli.c:423)
==19870==    by 0x1D572A: main (php_cli.c:1325)
==19870==  Block was alloc'd by thread #1
==19870== 
==19870== Possible data race during read of size 1 at 0x20D4171C by thread #1
==19870== Locks held: none
==19870==    at 0x4C2C873: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x4C2CB48: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x4C30C30: pthread_mutex_destroy (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x15F297DC: pthreads_monitor_free (monitor.c:126)
==19870==    by 0x15F324D0: pthreads_routine_wait (object.c:127)
==19870==    by 0x15F324D0: pthreads_start (object.c:380)
==19870==    by 0x15F26F44: zim_Thread_start (thread.h:97)
==19870==    by 0x35ADF9: dtrace_execute_internal (zend_dtrace.c:107)
==19870==    by 0x3FA51C: ZEND_DO_FCALL_SPEC_HANDLER (zend_vm_execute.h:844)
==19870==    by 0x3B07EA: execute_ex (zend_vm_execute.h:414)
==19870==    by 0x35AC7F: dtrace_execute_ex (zend_dtrace.c:83)
==19870==    by 0x15F279C4: pthreads_execute_ex (php_pthreads.c:143)
==19870==    by 0x40B9E8: zend_execute (zend_vm_execute.h:458)
==19870== 
==19870== This conflicts with a previous write of size 4 by thread #25
==19870== Locks held: 1, at address 0x935B328
==19870==    at 0x7DE6F21: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:76)
==19870==    by 0x7DE6F21: pthread_mutex_unlock (pthread_mutex_unlock.c:315)
==19870==    by 0x4C2D348: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x4C30C5C: pthread_mutex_unlock (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x15F2956C: pthreads_monitor_unlock (monitor.c:61)
==19870==    by 0x15F2CD1C: pthreads_prepared_startup (prepare.c:687)
==19870==    by 0x15F32180: pthreads_routine (object.c:482)
==19870==    by 0x4C2F967: ??? (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x7DE3609: start_thread (pthread_create.c:334)
==19870==  Address 0x20d4171c is 12 bytes inside a block of size 96 alloc'd
==19870==    at 0x4C2BDE7: calloc (in /usr/lib64/valgrind/vgpreload_helgrind-amd64-linux.so)
==19870==    by 0x15F294C6: pthreads_monitor_alloc (monitor.c:33)
==19870==    by 0x15F32400: pthreads_routine_init (object.c:118)
==19870==    by 0x15F32400: pthreads_start (object.c:376)
==19870==    by 0x15F26F44: zim_Thread_start (thread.h:97)
==19870==    by 0x35ADF9: dtrace_execute_internal (zend_dtrace.c:107)
==19870==    by 0x3FA51C: ZEND_DO_FCALL_SPEC_HANDLER (zend_vm_execute.h:844)
==19870==    by 0x3B07EA: execute_ex (zend_vm_execute.h:414)
==19870==    by 0x35AC7F: dtrace_execute_ex (zend_dtrace.c:83)
==19870==    by 0x15F279C4: pthreads_execute_ex (php_pthreads.c:143)
==19870==    by 0x40B9E8: zend_execute (zend_vm_execute.h:458)
==19870==    by 0x36D146: zend_execute_scripts (zend.c:1428)
==19870==    by 0x2FDF97: php_execute_script (main.c:2471)
==19870==  Block was alloc'd by thread #1
.....

Perhaps because of the parallel execution of the same code on different cpu cores allocation takes slightly more than planned? It may be a problem in locks?

krakjoe commented 8 years ago

This not normal. This is mistake. This is a memory leak. All resources must be freed!

I've told you how it works ... there's no mistake, and no important leak ... relying on process shutdown to free some memory is something PHP has always done, and will always do ... nothing to do about it ...

Worth mentioning that PHP does make an effort to free all that it can, but it's sometimes the case that some third party API (for example libxml, curl) may not be able to free until process shutdown.