Introdução a objetos e referências na memória PHP

Publicados: 2022-03-11

Eu escrevi este artigo pela primeira vez enquanto estudava para minha certificação PHP em um esforço para entender melhor como o PHP gerencia variáveis ​​e objetos na memória. Depois de muita pesquisa, percebi que não era fácil encontrar respostas para minhas dúvidas, então, assim que terminei, decidi documentar as informações para que as pessoas pudessem encontrar tudo em um só lugar.

Neste artigo, falarei sobre como as referências de objetos e variáveis ​​são controladas na memória, pois esse é um assunto que pode gerar discussões e opiniões divergentes. Uma questão a ser ponderada é: “Por padrão, os objetos são passados ​​por referência ou por cópia no PHP?” Vou falar primeiro sobre quais referências não estão no PHP; em segundo lugar, discutirei o que são e, finalmente, examinarei como o coletor de lixo funciona em PHP.

Como o PHP cria objetos na memória ao executar uma instrução como $a = new Foo(); ? Hoje em dia, a memória não é um recurso tão caro e limitado como era no passado. No entanto, ainda é importante que bons desenvolvedores PHP conheçam e entendam como variáveis ​​e objetos são gerenciados internamente durante a execução de sua aplicação.

Objetos e referências na memória PHP e coleta de lixo PHP

Objetos e referências em PHP

Muitas pessoas dizem – em livros PHP e online – que objetos em PHP são passados ​​por referência por padrão. Outros dizem que objetos em PHP são alocados por cópia. Para descobrir qual afirmação está correta, primeiro temos que analisar o que é (e o que não é) uma referência em PHP.

O que não são referências em PHP?

Mais importante do que saber o que são referências no PHP é saber o que elas não são. No PHP, as referências não são ponteiros no estilo C; você não pode fazer operações aritméticas com referências como pode fazer com ponteiros C. Por quê? Porque, diferentemente de C, as referências PHP não são realmente endereços de memória, pois não são números que indicam um local de memória. Mas então, o que são referências?

O que são referências em PHP?

Em PHP, referências são “aliases” que permitem que duas variáveis ​​diferentes leiam e escrevam um único valor. Dito de outra forma, são mecanismos que permitem o acesso ao mesmo valor de variáveis ​​com nomes diferentes para que se comportem como se fossem a mesma variável. Tenha em mente que no PHP, os nomes das variáveis ​​e o conteúdo das variáveis ​​são duas coisas totalmente diferentes, ligadas no que é chamado de “tabela de símbolos”. Então, quando criamos uma referência, ela simplesmente adiciona um alias para essa variável na tabela de símbolos. Suponha que temos o seguinte código:

 $a = new Foo();

Quando a instrução acima é executada, a variável $a é criada na memória, um objeto do tipo Foo é criado na memória e uma entrada é adicionada à tabela de símbolos que indica que a variável $a “referências” (ou está relacionada a , ou aponta para, ou como você quiser chamá-lo) o objeto Foo , mas não é um ponteiro para esse objeto, por si só. Conceitualmente, temos algo como esta ilustração:

Referências de objetos na memória PHP

Pop quiz: O que acontece se executarmos isso?

 $b = $a;

Não é que $b se torne uma referência de $a ; nem podemos dizer que $b é uma cópia de $a . O que realmente aconteceu é que criamos uma nova variável $b na memória e então adicionamos uma nova entrada na tabela de símbolos indicando que a variável $b também referencia o mesmo objeto Foo que $a faz. Então, visualmente, temos algo parecido com o que é mostrado nesta ilustração:

Referências de objetos na memória PHP

Agora, se executarmos:

 $c = &$a;

Teremos criado uma terceira variável $c na memória, mas não uma nova entrada na tabela de símbolos para $c . Em vez disso, na tabela de símbolos, é registrado que $c é um alias para $a , então ele se comportará de forma idêntica, mas $c não é um ponteiro para $a — ao contrário de C, que cria algo chamado ponteiros para ponteiros . Para visualizar, temos algo parecido com o que é mostrado nesta ilustração:

Diagrama de variáveis ​​e seus aliases

Assim que quisermos modificar o valor de qualquer uma dessas três variáveis ​​(ou seja, escrever um novo valor), o PHP terá que criar uma nova estrutura z_val na memória para separar o conteúdo da variável $b e o par $a / $c para que cada um possa ser modificado independentemente sem afetar o valor do outro. Então, se adicionarmos a seguinte linha ao script anterior:

 $b = new Bar();

Na memória, teremos uma situação conforme representada na ilustração a seguir:

Representação gráfica da situação descrita acima

Agora, vamos considerar um exemplo mais completo:

 <?php class myClass { public $var; function __construct() { $this->var = 1; } function inc() { return ++$this->var; } } $a = new myClass(); // $a "references" a Foo object $b = $a; //b also references the same Foo object as a //($a) == ($b) == <id> of Foo object, but a and b are different entries in symbols table echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); $c = &$a; //$c is an alias of $a //($a, $c) == <id> of Foo object, c is an alias of a in the symbols table echo "$c = ";var_dump($c); $a = NULL; //The entry in the symbols table which links "$a" with Foo object is removed //Since that entry was removed, $c is not related to Foo anymore //Anyway, Foo still exists in memory and it is still linked by $b echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); echo "$c = ";var_dump($c); echo "$b->var: ".$b->inc(); echo "$b->var: ".$b->inc(); $b = NULL; //The entry in the symbols table which links "$b" with the Foo object is removed //There are no more entries in the symbols table linked to Foo, //So, Foo is not referenced anymore and can be deleted by the garbage collector echo "$b = ";var_dump($b);

A saída produzida pela implementação do script acima é:

 $a = object(myClass)#1 (1) { ["var"]=> int(1) } $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = object(myClass)#1 (1) { ["var"]=> int(1) } $a = NULL $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = NULL $b->var: 2 $b->var: 3 $b = NULL

Coleta de lixo PHP

Por fim, vamos ver como funciona a coleta de lixo do PHP, já que foi introduzida na versão 5.3. Um objeto ou variável na memória do PHP será removido pelo coletor de lixo do PHP quando não houver referências a esse objeto na tabela de símbolos. Ou seja, o PHP mantém um contador de referências de um objeto desde o momento em que é criado para que durante a execução do script PHP, o contador incremente e diminua esse contador de referência com base nas variáveis ​​que estão “apontando” para ele. Uma vez que a contagem de referência atinge 0 (ou seja, nada está referenciando aquele objeto e, portanto, não está sendo usado), o PHP marca esse objeto como removível, para que na próxima passagem do coletor de lixo do PHP, ele seja removido da memória , liberando esse espaço para reutilização. Se você quiser mais detalhes sobre como funciona a coleta de lixo do PHP, leia esta documentação.

Considerações finais

Espero ter esclarecido um pouco como o PHP trata objetos e variáveis ​​na memória e como ele “seleciona” os objetos que devem ser removidos pelo coletor de lixo do PHP.

Agora que você entende como o PHP gerencia variáveis ​​e objetos na memória internamente, pegue seu laptop e comece a experimentar algum código para provar o que você aprendeu. Tente brincar com variáveis ​​e referências. Além disso, experimente como alterar o valor de uma variável pode afetar o valor de outra que a referencia. Aqui vai uma pergunta para você: Quais serão os valores de $a e $b depois que o código abaixo for executado?

 $a = '1'; $b = &$a; $b = "2$b";

Se você estiver interessado em ler mais sobre os recursos de desempenho do PHP, confira este post do colega Toptaler Vilson Duka.