What happens in detail in the destruction of a variable?

Asked

Viewed 864 times

16

I wonder what really happens behind the command unset in a variable in the PHP.

I believe this happens in memory, but I would like to deepen my knowledge of this and get the certainty of what happens through someone’s experience.

  • 14

    The variable is sent to the antimatter of space, causing what we know as "black holes".

  • 1

    I don’t remember all the details but basically the variable is only considered inaccessible to the program, nothing is released in memory. PHP has a dynamic implementation of variables, I don’t remember well but if I’m not mistaken all variables are simply elements of a structure hash. I know some forms of variables are like that but I can’t remember if they’re all.

  • 2

    Rogers, it depends on the context of the variable, if it is part of an array, only the index reference is removed, if it is a normal variable and it is in the scope of another function, it continues to exist, it will depend on where it came from, what type it is, and where it goes or went. But it is usually released from memory at the end of the script execution, this release of memory is nothing more than losing the access reference to the corresponding bits, which will be used again to record other information with another reference, when you say something is in memory, it means you have stored reference.

  • You see that complicated thing, you tell me that yes is deleted from memory, while @bigown says maybe not. I think I’ll stick with that doubt. cool would be to see what is behind this function.

  • 2

    @Rogerscorrêa very calm at this time :) the question is new yet, great chances to come something more defined with time, and in answer format.

  • It is more associated with hardware than software, it is like a hard drive, even formatting, the data remains there until it records over again, and creating a new data access reference...

  • 2

    @Rogerscorrêa Different things were said. At the end of the script execution I am sure it is released from memory. The script closes and everything is released. But the question is about the unset, when it occurs the memory is not released. Anyway, for you to have something accurate and reliable you will have to wait for someone more experienced to respond. It may not occur, it may take time. Another way to find out is to take PHP fonts and see what happens. It’s not easy but it’s a solution if the answer is important.

Show 2 more comments

3 answers

9


php is a language produced in C and C++ today your source code is available on github information about unset use may not found on the official php page. The first concept that we must understand is what a variable really is, the main confusion begins when it does not assimilate this concept very well. A variable is a reference that points to a memory address. What do you mean?!? Imagine a memory with a total storage capacity of 16 bits, you can store at most 2 bytes (2x8 = 16), you have the address 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 as the smallest unit of measure we have is the bit if we were to divide the memory we could divide into at most 16 pieces. Joãozinho, Maria, Sileno and Manuel want to store things inside. Joãozinho wants to store 8 bits. And ask the operating system to make that space available to him. The operating system sees that it is possible to store and reserve 8 bits for Johnny and says to Joaozinho: Johnny, I’ve already reserved his space he starts at position 1. Here then Johnny, naughtily, not to forget where he can store a nickname for that address, to make it easier to memorize from where he can store the data and the nickname of var1. Then arises the concept of variable. It serves to store a memory address that for humans is an easier name to assimilate and for machines the location of a memory area.
Changing into children (in C):

void *var1 = malloc(8); // malloc pede ao so que reserve um espaço de 8 bits
// var1 recebe o endereço inicial da posição de memória que o so reservou.

Inside var1 then Johnny can store any value, up to 8 bits.
Maria also wants to store 8 bits and asks the OS to reserve this space he informs Maria that the initial position of her address is 9 she does the same as Johnny creates a nickname for that address var2.
At this time all memory space is allocated. The operating system has no way to allocate anything else to anyone. Then, Joaozinho gives a memory release and returns to the operating system the 8 bits and Maria also returns the 8 bits and the operating system again has 16 bits free. Changing into children (in C):

free(var1); // libera a memória que começa no endereço 1
free(var2); // libera a memória que começa no endereço 9

Sileno then asks for 16 bits for the OS the OS reserves this data and informs that the initial position of its address is 1 it does the same as Johnny and creates a nickname for that address var3.

Manoel tries to allocate 1 bit, the OS informs that it cannot allocate and returns to Manoel the position 0 (NULL).

However, managing memory in this way is possible (C is like that), but it requires a lot of attention and can create several problems if you forget to release the memory. The allocation of small blocks could even cause that even having space it was not possible to allocate memory. For example:

Joãozinho needs 8 bits and the OS has 9 bits available, but allocated var1 = address (1,2,3); var2 = (9,10,11); var3 = (14) In total 7 bits were allocated, but to allocate the 9 bits the has to take a sequential space and as this fragmented it despite to have the space has no way to allocate the data and Johnny does not have his allocated space receiving a NULL as response to the address initial.

New concepts were then created so that human failure in relation to memory management was minimized.

Garbage Collector allows you to reserve a memory space for your program, you don’t need to explicitly release it when you don’t use it anymore, because it checks the elements that are not used by anyone and removes.

Heap and Stack are other memory management concepts that are very interesting.

Heap is the memory reserved for dynamic allocation. hum?!? Yeah, in that the application already allocates a block of initial memory and instead of taking direct memory of the OS, you take that reserve of the application.

Stack (according to Nokia’s Wiki): These are memory regions where the data is inserted and removed following the data structure 'last entered first left'. Objects and variables declared within the scope of functions and methods are in this memory region.

Okay, but the question was about the unset, rolled over, talked about C/C++ and a bunch of crap and didn’t answer the question...

All right, php has a heap, the Zend MM which is responsible for memory management, PHP also implemented the Garbage Collector and unset? (the code is too big, gave laziness to search). Did not find ?! I found unset functions, for Session, array, Property and the unset? (well I’m not curling just found in the last inconformity search for not being able to find the answer) i found with Google help (novelty unset and all the rest...) the unset who calls the zend_hash_del After that it became clear how the unset worked because I found another document the final piece of the puzzle. And I come to share with all of you how php’s memory management works and the damn unset (I didn’t write it and it’s in English). this document, this is another link to the same document, pity that I only found at the end when I was finishing writing the topic.

(It’s not a quote, I’m just going to skip the point here, I’m marking to highlight, resume after that part)

How much if you have an 8GB memory you can split uses memory in many 1 bit spaces. However, we entered a problem called architecture as it is, that matter of the 32bits and 64 bits. Our OS has to "count" in 32 bits he can only count up to 2 32 or 4294967296 this is the maximum number that the OS will be able to count because it is limited to form a number of up to 32 bits. But if you split your 8GB memory you will get more than 4294967296 pieces. What happens then ?! The OS will only use 4294967296 pieces and the rest of the memory will become idle and sedentary. And how to solve that? 64-bit architecture 2 64 lets you form 18446744073709551616 numbers. Ai you can use your 8GB and even more. (I ran away from the subject here, but I’ll leave the question of architecture)

  • 1

    Very good your answer, well explanatory.

  • 4

    The answer is very interesting, I’m just waiting for her to answer what was asked.

  • Really good, I get it, so this is what happens!

9

TL;DR

When using unset() PHP decrees an internal counter of its basic structure for storing composite types called zval. When this counter reaches zero, the memory used is automatically released.


To answer this type of question in the most appropriate way first you need to know how PHP manages its variables, the most essential of the language features.

What exactly are variables in PHP?

Variables in PHP are represented internally by a container called zval, a C structure defined in the file zend. h from the PHP source code:

struct _zval_struct {
    /* Variable information */
    zvalue_value value; /* value */
    zend_uint refcount__gc;
    zend_uchar type; /* active type */
    zend_uchar is_ref__gc;
};

The field valuerepresents the effective value (integer, string, object...). Next we have two fields related to memory management: is_ref and refcount.

refcount is an integer indicating how many symbols point to that zval. A symbol is typically a PHP variable (such as $to) but can also be an internal variable to/from C, since others zval are also used by engine.

is_ref is an integer representing a boolean value. By default it is set to zero which means that zval has not yet been affected by a reference (in PHP syntax &$a). when set to 1 (one) means that zval is a reference what we will see below characterizes a great change of appearance when PHP operates that zval.

Composite Types

In PHP we have many things that are zval. For example, how many zval you can count on this little fragment below:

$a = ‘foo’;

Obviously the simplest answer is the correct one:.

If we were to act in an image, we would have something like this:

inserir a descrição da imagem aqui

And as for this little code?

$a = array('foo'=>'bar', 42);

Here we have three zval: A string bar, the whole 42 and the array that encapsulates them. Illustrating, we have:

inserir a descrição da imagem aqui

Note the keys in the array. foo and 1 are not zval, only its values are. Now let’s see how they work with objects:

class Foo {
        public $a = 42;
        protected $bar = 'default';
}

$obj = new Foo;

Here too three were created zval: One for the object in the symbol $obj and one for each of the attributes, $to and bar.

You will notice that, from this point of view, objects behave like arrays because, like them, they are compound types, so they have zval for themselves only one for each type they contain. Very simple!

How PHP manages variables?

Now let’s see how PHP manages all these zval when you, the programmer, use them daily.

Let’s skip the references for a moment and focus on how smart PHP is at not duplicating memory from zval when you copy content from one variable to another:

$a = 'foo';
$b = $a;

inserir a descrição da imagem aqui

In the image above the container zval is represented in yellow. It is what consumes memory and is what you (or PHP) should prevent against duplicities and release them as soon as possible.

In gray are represented the symbols, the svariáveis PHP. the symbols also consume memory, but in such small quantities that most of the time you need not worry.

By this we can safely say that both scripts below consume the same amount of memory:

// Script 1
$a = 'foo';
$b = $a;

// Script 2
$a = 'foo';

As you must have perceived the field refcount was increased when $b was pointed to another zval. refcount is the "trick" behind the memory management in PHP variables. Through it we know how many symbols point to the same container zval.

Copy on Write

Copy On Write (abbreviated by COW) is another trick designed to save memory more generically used in software engineering. It means that PHP will copy the memory or better saying allocate a new region of/in memory when you write a symbol if this is already pointing to another zval. Ex: : $a = "foo"; $b = $a; $a = 17;

inserir a descrição da imagem aqui

Another example, important to the topic scope:

$a = "foo";
$b = $a;
$c = $b;
$b = "bar";
unset($a);

inserir a descrição da imagem aqui

Notice how PHP manipulates the field refcount. This is incremented and decremented as symbols point or fail to point to a given zval. Only the required memory is allocated.

Also note that unset() does not necessarily release memory. unset() will only decrease the value refcountin a.

If, and only if, the value of refcountzero is that Php knows that no other symbol is pointing to that zval and then will release it automatically and immediately.

Reinforcing that all this behavior assumes that references are not being used, hence refcount have always remained at zero throughout the detailing of the process which is simple and easy to understand.

Extra Credit! If you want to delve into the subject, the Xdebug offers the function xdebug_debug_zval() through which it is possible to extract both values refcount and is_ref.

Let’s see now an example with arrays:

$a = array("foo"=>"bar", 1 => 42);
$b = $a["foo"];
$c = $b;
$b = 18;
unset($a['foo']);
$a[1] = $b;

inserir a descrição da imagem aqui

As you can see the same behavior is applied and the same important maintained: Only when refcount reaches zero the zvalrespective is released. Never before.

And again, given the main scope of the topic, see what happens to unset(). The zvalreferring to the associative index foo the array was deleted, but as there was another symbol previously created ($c) the zval associado à string **bar** foi mantido, tendo apenas seurefcount`decrescido.

Functions / Methods

Yes, functions and methods are the same thing. That said, let’s see how they behave with respect to symbols and zval

What should be borne in mind is that when a function is created the scope of the same created together. And with the exception of global variables, those created within a function cannot be accessed from outside of it.

Finally, function arguments as well as their return value have their respective refcountincreased instead of having others zval created. Let’s look at an example:

<?php
function foo($var)
{
    $var = "bar";
    return $var;
}

$a = "foobaz";
$b = foo($a);

inserir a descrição da imagem aqui

Stack starts with symbol setting $to and its respective zval for a string to be used as argument of the function to be invoked.

Next we have the invocation of the receiving function $to as argument. Note that the refcount account for 3 (three): one for the zvalstring foobaz and two others for the function argument ($var) and its local scope.

Once with the flow within the function we have the refcount of the symbol $var (and consequently of $to) decreasing as $var, now, it’s relocated to another string. And now we have two zval different. Then we have the return of the function. The symbol $b is created and instead of a third zval be created only the refcountof $var is incremented by pointing to $b.

Finally, when the function is finished both refcount of the two zval are decreasing as both the scope and the consumption stack itself are destroyed in the same way as the unset().

And all this automatically, without the need to allocate and free memory manually.

References

All that has been said so far is limited to the "classic" use of PHP, let’s make things a little more interesting with the references.

Reminder: references are set in PHP when you use the symbol & (ampersand)

Quick example:

$a = 'string';
$b = &$a;
$b = 1;

Everyone with knowledge of language should already know just by looking at the result of this snippet: $to and $b have the same value: the whole 1

Internally, when used, references tie the symbols together ($to and $b) at the same zval. You could even say it’s the same as when doing $b = $a.

In fact, that’s exactly what happens. $a = $b or $a = &$b results, internally, in the same thing. The difference is how the references profoundly change the behavior of PHP when the Copy On Write occurs, that is, on the line $b = 1:

inserir a descrição da imagem aqui

When defining the symbols $to we created a zvalfor the string string. When we define the symbol $b and use the ampersand (&) the field is_ref is increased and the behavior of refcount continues the same, that is, continues to be increased to represent the number of symbols pointing to that zval.

But the flag is_ref will change the behavior of Copy On Write PHP when you change or change the symbol by no longer separating the value of each symbol in two zvaldistinct, but instead alter it directly, giving the impression that both $to and $b are connected.

Simple and efficient but requiring double attention in some cases. Let’s look at another example that demonstrates the behavior change of all this "duplicate or not duplicate memory":

$a = "string";
$b = &$a;
$c = $b;

inserir a descrição da imagem aqui

Pay close attention to the third line $c = $b;

$b is a reference of $to and both point to the same zval. By creating the symbol $c it can be imagined that by this point to $b he would also be pointing to $to and the refcountwould only be increased to 3.

But is not what happens. Because of the precedence PHP is forced to create a new zvaland duplicate the string string

So much is true that if the value of $c is amended after being associated with $b, the value of $b (and consequently of $to) would not be affected:

$a = "string";
$b = &$a;
//$b = 'foo';
$c = $b;

var_dump( $a, $b, $c ); // string(6) "string" string(6) "string" string(5) "Bruno"

To highlight even more, if we were to unveil the line that changes the value of $b we have:

string(3) "foo" string(3) "foo" string(5) "Bruno"

In short: $c is not a reference to $b although it was previously defined as a reference to another symbol ($to).

And this is the first precaution you should take when using references because PHP copies memory in places you wouldn’t normally expect it to be.

One more example, now with functions:

<?php
function foo(&$var)
{
    $var = "bar";
    return $var;
}

$a = "foobaz";
$b = foo($a);

inserir a descrição da imagem aqui

See how PHP doubled the string bar because of the reference. Memory duplication occurred not when the assignment $var = 'bar';was made, after all, $var is a reference, but rather when the function returns, which is the value and not the reference now defined.

At that time the zvalis duplicated because when you change what the function will return you are not changing what submission received, even if it is a reference.

With this in mind it is necessary to remember that references do not necessarily save memory. With them the memory duplication is only delayed compared to a normal assignment, by value.

But sooner or later, a new zvalshould be created, unless you are very careful and use faithfully the reference from beginning to end, without ever breaking it, explicitly or implicitly.

Finally the last of the care to have with references;

function foo(&$var)
{
    if (strlen($var) > 3) {
        return $var;
    } else {
        $var .= '_uppercased';
        return strtoupper($var);
    }
}

$value = 'barbaz';
echo foo($value);

Yes, that’s a very stupid code, but it’s useful to comment on. You would be able to guess when exactly PHP would need to create a new zvaland duplicate the content?

Did you notice that $var was passed by reference to the function foo(). $var has a is_refequal to 1. But what happens when you invoke both, strlen() and strtoupper()?

If you check the manual you will see that these arguments are passed by value and the argument of our function is passed by reference. Have you seen where this goes? PHP will duplicate memory with each invocation of each of these functions.

Yes, PHP has no way of knowing if strlen() will attempt to modify the argument.

What would happen if he had any way of knowing? $var would be amended since strlen() does not accept references as arguments.

And since that’s not what we want, PHP is forced to duplicate the zval to work with the value of the variable passed. However, in this case at least the is_ref would be set to zero, before and after invoking said functions, and only then the memory used by zvalwould be released.

This is a waste and you’ve been fooled by the references again. Go ahead and admit it! The translator here admits and at this point is lying on the icy floor crying. p

If $var had it not been passed by reference no duplication would have happened (of course, if you had done your job as a programmer and had dealt decently with that concatenation there).

But there’s no reason to panic. Duplicate that barbaz would impact nanoseconds today. But that’s not what happens with large and complex arrays, true buffoons in terms of performance.

In such cases the values would not be copied, they would only have their refcount incremented. However the array whole would be copied.

To give you an idea, an array with a million entries would take about 0.3 seconds in PHP 5.5.

It seems little, but keep in mind that the vast majority of PHP uses runs at least 100 milliseconds faster And these 0.3 seconds takes into account A request which is VERY different from a real Application with thousands of people accessing the resource in question. And worse! At the same time

Memory leakage and Garbage Collection

first let’s see quickly what it would be Garbage Collection. Garbage Collection, in a free setting, it is a mechanism that tracks the use of objects and releases their memory media once they can no longer be reached in the current scope or are no longer being used (by the programmer).

Bringing this definition to PHP and all that has been said refcount applied to zval that, in fact, release the memory when it reaches zero.

Since the beginning PHP provides a mechanism for Garbage Collection for its variables, however, we have the Zend Garbage Collector (or Zend GC) introduced in PHP 5.3 which, unlike what is thought does not release the memory used (who does this is PHP itself) but takes care of the circular refrains by you.

The Manual for PHP has a very detailed section, including images, on how the garbaging.

Circular References appear in composite types such as arrays and objects. When two compound types refer to each other or when a compound type refers to itself, we have a circular reference. This is bad because it is hard to compute when exactly they start or should be released.

Until before PHP 5.3 there was no way to track circular references and when you released your variables, with unset(), for example, forcing your zeroes refcount, PHP left some "traces" of entities as if they still existed. Ex:

// Criamos duas entidades (objetos)

$a = new ObjA;
$b = new ObjB;

// Criamos a referência circular

$a->b = $b;
$b->a = $a;

// Decrementamos os refcount
unset($a, $b);

/**
 * Deste ponto em diante o não se é mais possível
 * obter os dois zval acima, mas eles ainda existem
 * na memória e o PHP, até antes da versão 5.3
 * nunca iria liberá-los.
 */

inserir a descrição da imagem aqui

The symbols are created and when the properties are set we have new zval being created, one for each property of each symbol and the refcount incremented.

unset() destroyed the symbols and decreed the refcount as expected. However those created by setting the properties remained there, forgotten.

With PHP 5.3 a complex system has been introduced specifically to track these cases that are becoming more and more frequent as more and more programmers manage more and more objects in their code. It’s easy to have two objects in circular reference and you obviously don’t even notice.

Now PHP automatically tracks circular references and frees up the memory they use from time to time. If you happen to notice a circular reference and want to force its release PHP provides the function gc_collect_cycles() specifically designed for this.

Let’s see how in one more example:

// Criamos duas entidades (objetos)

$a = new ObjA;
$b = new ObjB;

// Criamos a referência circular

$a->b = $b;
$b->a = $a;

// Decrementamos os refcount
unset($a, $b);

/**
 * Forçamos o PHP a limpara a referência circular
 * e exibimos quantos zval foram liberados
 * pelo garbage collector
 */
$cleaned = gc_collect_cycles();

echo $cleaned; /* 2 */

Circular references consume memory, but this is not so worrying in a Web environment since PHP cleans all resources anyway, including circular references, at the end of the Request.

The problem becomes much more apparent in long-cycle CLI applications, such as Phpunit testing. Thanks to Zend GC your memory curve should remain relatively simple.

Original Article: Web and PHP

Translation, adaptation and complement: Bruno Augusto

  • Finally the translation was ready u.u'

  • I can’t see the images of Imgur through the company firewall. =\

  • 1

    Soon I edit (or someone else, who knows) to replace the external images by uploads here in the same SOPT. It’s how I took a few days to slowly translate and gather complementary information I wrote the article / reply on Stackedit.io

  • Very good this post, clear and explained, congratulations!

  • Oh stop it you! :p

  • I uploaded the images here to SOPT but it helped a lot because the site also sends to an Imgur account of its own.

Show 1 more comment

2

Well, I ran some tests and look what happened...

inserir a descrição da imagem aqui

The code:

<?php

var_dump($xacrinha);

print "<br/><br/><br/><br/><br/><br/>";

$xacrinha = 12;

var_dump($xacrinha);

print "<br/><br/><br/><br/><br/><br/>";

unset($xacrinha) ;

var_dump($xacrinha);

?>

Looking at this what I understood? I understood together the tips of the comment staff that the unset does not remove from memory the variable, but probably it declares the variable as null...interesting that from what I realized when I put the dollar in front it already allocates the memory to my variable. That is in PHP, unset just declares my variable as null. I know there may be more to explain and I will wait for a more consistent response...I thank the staff for their comments.

I did another test and the doubt continues:

inserir a descrição da imagem aqui

Look at the code, declare a variable as null is different from unset, I’m coming to the conclusion that it really is yes, deleted from memory.

<?php

var_dump($xacrinha);

print "<br/><br/><br/><br/><br/><br/>";

$xacrinha = null;

var_dump($xacrinha);

print "<br/><br/><br/><br/><br/><br/>";

unset($xacrinha) ;

var_dump($xacrinha);

?>
  • I have not read the PHP source code to know how the exact unset works and I may be giving a misleading explanation. Creating a variable is to reserve allocating a memory address, the variable is a way to point to that physical of memory, within that area you store data. Once used you should release this address, but PHP does not work by default with the release of this memory address immediately. I believe that PHP stores the variables in use in heap and unset just remove this variable from that heap. Continue...

  • I don’t know @Silenobrito, it seems to me much more that PHP replaces the value d allocated variable by the value null.

  • php in turn allocates an amount of initial memory to execute what it needs that can grow according to the need of the executed instruction and from time to time it must execute a kind of Garbage Collector to if necessary release the unused space. unset in this way serveria to mark for php what can be reused when it has to use more memory resources. And avoids memory tracking by allocating several small spaces in different memory locations.

  • Yes, it might be. But would that override the question declaring null the variable?? Still doing the continuous test with this understanding and maybe he even has this feature of knowing what can be reused. Now you’re gonna start tying a knot in the cerebellum.

  • I didn’t understand the question of declaring the variable null, a variable as null exists and occupies space (based on C)

  • Yes for that very reason. It is not removed, deleted, deleted from memory, simply as I understand it, unset in PHP only says that the variable Xyz = null.

  • 2

    Not necessarily, a variable when set to null exists, the variable with unset ceases to exist. To better understand I advise you to read about the concept of Garbage Collector (http://pt.wikipedia.org/wiki/Coletor_de_lixo_(inform%C3%A1tica) and (http://javafree.uol.com.br/artigo/1386/Introducao-ao-Garbage-Collection.html).

  • Thank you @Silenobrito!

  • @Rogerscorrêa do not know about the memory, but when you var_dump a non-existent variable appeared a notice: a variavel está definida, but how PHP does to appear only one notice and not a fatal error? I believe that the compiler interpreting temporarily assumes that the value of nonexistent variables is null, but still without defining them, so he can continue running the code "without errors". based on this I believe that PHP replaces missing vars with null, so in var_dump($naoExiste) the value of $naoExiste is temp. null

  • Ah, and according to NULL type documentation: http://www.php.net/manual/en/language.types.null.php A variable is null when: she was unset erased(), it has not received any value yet, or has been set with null.

  • I think you’re confused between what is the variable and what is the value it contains. When you use unset, or assign another value to a variable (such as null), the memory occupied by the previous value is marked as "collectible" by the GC.

  • @bfavaretto I know the difference between heap memory and stack. The problem is knowing what PHP does and where it deletes it or simply set a value or not.

  • The variable ceases to exist or it receives the value null??

  • With unset it ceases to exist.

  • But where is the proof of this?

  • 3

    If I had known, I would have posted an answer :)

  • Could I make a comparison between C and PHP where my variable takes up space??

Show 12 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.