- URL: https://www.laruence.com/en/2011/03/04/1894.html
- Please include attribution when republishing.
First let's look at a question: the output of the following code,
var_dump(memory_get_usage()); $a = "laruence"; var_dump(memory_get_usage()); unset($a); var_dump(memory_get_usage());
The output (on my personal machine, it may differ depending on system, PHP version, and loaded extensions):
int(90440) int(90640) int(90472)
Note that 90472-90440=32, and so various conclusions have been drawn. Some say PHP's unset doesn't actually release memory; some say PHP's unset only really frees memory when releasing big variables (large strings, large arrays); others go further and say discussing memory at the PHP level is meaningless.
So, does unset release memory or not? Where did these 32 bytes go?
To answer this, I'll approach it from two angles:
Where did these 32 bytes go
First we have to break a mental habit: unlike C, in PHP you don't get memory allocation only when you explicitly call memory-allocation APIs.
That is, in PHP there are many memory-allocation processes we can't see.
For example for:
$a = "laruence";
the implicit allocation points include:
1. allocate memory for the variable name, store in the symbol table 2. allocate memory for the variable value
So you can't just look at the surface.
Second, don't doubt it: PHP's unset does release memory (of course this interacts with references and refcounts; see my earlier article Variables Separation/Reference in PHP Internals for that part). But this "release" isn't the C-programming sense of release, it's not handing memory back to the OS.
For PHP, it provides its own set of memory-management APIs similar to C's:
emalloc(size_t size); efree(void *ptr); ecalloc(size_t nmemb, size_t size); erealloc(void *ptr, size_t size); estrdup(const char *s); estrndup(const char *s, unsigned int length);
These APIs correspond to C's APIs in meaning, and internally in PHP all memory is managed through these APIs.
When we call emalloc to request memory, PHP doesn't simply ask the OS for memory; it asks the OS for a large chunk of memory, then carves a piece out and gives it to the requester. That way, when more memory is requested later, it doesn't need to ask the OS again, avoiding frequent system calls.
For example:
<?php var_dump(memory_get_usage(TRUE)); //note we're getting the real_size $a = "laruence"; var_dump(memory_get_usage(TRUE)); unset($a); var_dump(memory_get_usage(TRUE));
Output:
int(262144) int(262144) int(262144)
That is, when we define the variable $a, PHP did not request new memory from the system.
Likewise, when we call efree to release memory, PHP doesn't return the memory to the OS; it puts this chunk into its own maintained free-memory list. For small chunks, more likely it's put into a memory cache list (postscript: in some PHP versions, e.g. the ones I verified — PHP 5.2.4, 5.2.6, 5.2.8 — when calling get_memory_usage() it does not subtract the available size in the memory cache list, so it looks like memory doesn't change after unset; see comments).
Now let me answer where those 32 bytes went. As I said, many memory-allocation processes are not explicit; look at the code below and you'll understand:
<?php
var_dump("I am Laruence, From http://www.laruence.com");
var_dump(memory_get_usage());
$a = "laruence";
var_dump(memory_get_usage());
unset($a);
var_dump(memory_get_usage());
Output:
string(43) "I am Laruence, From http://www.laruence.com" int(90808) //before assignment int(90976) int(90808) //yes, memory was released normally
90808-90808 = 0, normal. That is, those 32 bytes were taken up by the output function (strictly speaking, by the Header of the output).
An array that only grows
Hashtable is a core structure in PHP (to understand Hashtable, see my earlier article Arrays in PHP (traversal order)), and arrays are represented with it; the symbol table is also a kind of associative array. For the following code:
var_dump("I am Laruence, From http://www.laruence.com");
var_dump(memory_get_usage());
$array = array_fill(1, 100, "laruence");
foreach ($array as $key => $value) {
${$value . $key} = NULL;
}
var_dump(memory_get_usage());
foreach ($array as $key=> $value) {
unset(${$value . $key});
}
var_dump(memory_get_usage());
We define 100 variables, then Unset them one by one. Let's see the output:
string(43) "I am Laruence, From http://www.laruence.com" int(93560) int(118848) int(104448)
Wow, why is so much less memory here?
This is because for a Hashtable, at the moment it's defined, it's impossible to allocate enough memory blocks at once to hold an unknown number of elements. So PHP only allocates a small portion of memory blocks to the HashTable at initialization, and RESIZEs to expand when it runs out.
And a Hashtable can only expand, it never shrinks. For the example above, when we store 100 variables the symbol table runs out and does one expansion. After we unset those 100 variables one by one, the memory the variables occupied is freed (118848 - 104448), but the symbol table did not shrink, so the missing memory is taken up by the symbol table itself...
Now, do you have a preliminary understanding of PHP's memory management?
Be First to Comment