O que há de melhor em liberar memória com o PHP: unset () ou $ var = null

244

Sei que o segundo evita a sobrecarga de uma chamada de função ( atualização , na verdade é uma construção de linguagem), mas seria interessante saber se um é melhor que o outro. Eu tenho usado unset()a maior parte da minha codificação, mas recentemente examinei algumas classes respeitáveis ​​encontradas fora da rede que usam $var = null.

Existe um preferido, e qual é o raciocínio?

alex
fonte

Respostas:

234

Foi mencionado na página do manual não configurado em 2009 :

unset()faz exatamente o que seu nome diz - desativa uma variável. Não força a liberação imediata da memória. O coletor de lixo do PHP fará isso quando achar conveniente - por intenção, assim que os ciclos da CPU não forem necessários, ou tão tarde quanto antes o script ficaria sem memória, o que ocorrer primeiro.

Se você estiver fazendo $whatever = null;, está reescrevendo os dados da variável. Você pode liberar / reduzir a memória mais rapidamente, mas pode roubar os ciclos da CPU do código que realmente precisa deles mais cedo, resultando em um tempo de execução geral mais longo.

(Desde 2013, essa unsetpágina de manual não inclui mais essa seção)

Observe que até o php5.3, se você tiver dois objetos em referência circular , como em um relacionamento pai-filho, chamar unset () no objeto pai não liberará a memória usada para a referência pai no objeto filho. (A memória também não será liberada quando o objeto pai for coletado pelo lixo.) ( Bug 33595 )


A pergunta " diferença entre unset e = null " detalha algumas diferenças:


unset($a)também remove $ada tabela de símbolos; por exemplo:

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

Saídas:

Notice: Undefined variable: a in xxx
NULL

Mas quando $a = nullé usado:

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:

NULL

Parece que $a = nullé um pouco mais rápido que o seu unset()equivalente: atualizar uma entrada da tabela de símbolos parece ser mais rápido do que removê-la.


  • quando você tenta usar uma unsetvariável inexistente ( ), um erro será acionado e o valor da expressão da variável será nulo. (Porque, o que mais o PHP deve fazer? Toda expressão precisa resultar em algum valor.)
  • Uma variável com nulo atribuído a ela ainda é uma variável perfeitamente normal.
VonC
fonte
18
Observe que, se $whateverapontar para um objeto, $whatever = nullsubstitui o ponteiro, não o objeto em si, portanto, ele age basicamente da mesma forma que unset().
Gras Double
1
@VonC: a citação não definida no php.net a que você está se referindo não existe mais.
Jürgen Thelen 29/11
@ JürgenThelen verdade, mas o conteúdo dessa resposta antiga ainda parece relevante, não?
VonC 29/11
1
@VonC: Definitivamente. Só não tenho certeza sobre "ciclos de CPU não são necessários" e "antes de .. falta de memória" dispara a coleta de lixo. Consulte stackoverflow.com/q/20230626/693207 . Talvez você possa lançar alguma luz?
Jürgen Thelen 29/11
1
@Omar Editei a resposta: A página de manual não configurada de 2009 (vinculei à versão de 2009) inclui uma seção que não está mais presente na versão atual da mesma página.
VonC
48

unsetnão é realmente uma função, mas uma construção de linguagem . Não é mais uma chamada de função que a returnou a include.

Além de problemas de desempenho, o uso unsettorna a intenção do seu código muito mais clara.

Alex Barrett
fonte
É por isso que eu sempre os usei, pessoalmente, achei que eles pareciam melhores que $ var = null. A propósito, eu sempre usei NULL full caps ... mas agora não sei por quê?
Alex
1
@VonC: Sim, eu percebi isso, mas por que você pode usar letras minúsculas true, false e null?
Alex
3
@alex, você pode fazer isso com a definição. Por exemplo "$ test = 4; (não configurado) $ test;" - estranho, mas verdadeiro e retorna o valor de $ test antes de desmarcá-lo. Independentemente disso, o manual do PHP confirma que é uma construção de linguagem.
thomasrutter
5
@alex: PSR-2 requer letras minúsculas para todas as palavras-chave.
Tgr
2
@alex - palavras-chave PHP não diferenciam maiúsculas de minúsculas; você também pode escrever unsetcomo UnSeT, por exemplo. A comunidade optou por todas as letras minúsculas por uma questão de estilo, mas outras caixas ainda funcionam.
Mark Reed
35

Ao fazer um unset () em uma variável, você essencialmente marcou a variável para 'coleta de lixo' (o PHP realmente não tem uma, mas, por exemplo), para que a memória não fique disponível imediatamente. A variável não abriga mais os dados, mas a pilha permanece no tamanho maior. Fazer o método nulo elimina os dados e reduz a memória da pilha quase que imediatamente.

Isso foi por experiência pessoal e outras também. Veja os comentários da função unset () aqui .

Eu pessoalmente uso unset () entre iterações em um loop, para que eu não precise que o atraso da pilha seja de tamanho yo-yo. Os dados se foram, mas a pegada permanece. Na próxima iteração, a memória já está sendo usada pelo php e, assim, é mais rápida para inicializar a próxima variável.

William Holroyd
fonte
15
Definir algo como NULL pode ser benéfico se a memória necessária para armazenar o valor NULL for menor do que a necessária para armazenar qualquer valor que estava mantendo anteriormente. Por exemplo, uma sequência longa. Se a sequência não for constante e sua contagem de referência cair para zero, essa memória deverá ser liberada. Desativar é mais limpo - ele não mantém mais uma referência. Você precisa aguardar a coleta de lixo, mas é seguro tratá-lo como não ocupando memória, porque uma condição de pouca memória acionará a coleta de lixo.
thomasrutter
não podemos usar os dois? igual a nulo e depois desativado?
Nabeel Khan
2
@NabeelKhan Eu sugiro usar unset () dentro de loops e depois anulá-lo quando você sair do loop. Caso contrário, há um impacto no desempenho ao fazer as duas coisas dentro do loop. Se você não estiver usando loops, anule como já faz a lógica unset () nos bastidores.
William Holroyd 12/02
27
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";
?>

Por isso, parece que "= null" é mais rápido.

Resultados do PHP 5.4:

  • levou 0,88389301300049 segundos
  • levou 2.1757180690765 segundos

Resultados do PHP 5.3:

  • levou 1.7235369682312 segundos
  • levou 2.9490959644318 segundos

Resultados do PHP 5.2:

  • levou 3.0069220066071 segundos
  • levou 4.7002630233765 segundos

Resultados do PHP 5.1:

  • levou 2,6272349357605 segundos
  • levou 5.0403649806976 segundos

As coisas começam a parecer diferentes no PHP 5.0 e 4.4.

5.0:

  • levou 10,038941144943 segundos
  • levou 7.0874409675598 segundos

4.4:

  • levou 7,5352551937103 segundos
  • levou 6,6245851516724 segundos

Tenha em mente que microtime (true) não funciona no PHP 4.4, então eu tive que usar o exemplo microtime_float fornecido em php.net/microtime / Exemplo # 1.


fonte
7
Eu acho que seu teste é falho. O primeiro loop é uma reatribuição simples e o segundo loop destrói e recria o mesmo símbolo. Se o teste for refeito com uma matriz, unseté mais rápido. Eu tenho um teste que mais tarde verifica a existência no unsetcaso. Nesse teste, a configuração nullé marginalmente mais rápida. Teste: pastebin.com/fUe57C51
Knyri 19/07/2013
4
@ansur, sempre ligue gc_collect_cyclesantes de iniciar o cronômetro para obter resultados mais precisos.
Pacerier 7/08
@knyri, você pode fazer o link para isso?
Nabeel Khan
@NabeelKhan Não tenho mais os resultados desse teste; mas há um link para o código de teste no meu comentário anterior.
Knyri
19

Faz diferença com os elementos da matriz.

Considere este exemplo

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

Aqui, a chave 'teste' ainda existe. No entanto, neste exemplo

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

a chave não existe mais.

auris
fonte
18

Funciona de maneira diferente para variáveis ​​copiadas por referência:

$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5

$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null
RiaD
fonte
5
Estou codificando php há alguns anos e nunca vi "&" fazer referência à var original. Obrigado + 1 :)
Chris
1
$ a = 78; $ b = $ a; não definido ($ a); var_dump ($ b); // 78; var_dump ($ a); // Variável indefinida: a
zloctb
13

Em relação aos objetos, especialmente no cenário de carregamento lento, deve-se considerar que o coletor de lixo está sendo executado em ciclos inativos da CPU, portanto, presumindo que você esteja tendo problemas quando muitos objetos estiverem carregando tempo pequeno, a penalidade resolverá a liberação de memória.

Use time_nanosleep para permitir que o GC colete memória. Definir variável como null é desejável.

Testado no servidor de produção, originalmente o trabalho consumia 50 MB e depois foi interrompido. Depois que o nanosleep foi usado, 14MB foi um consumo constante de memória.

Deve-se dizer que isso depende do comportamento do GC, que pode mudar de versão para PHP. Mas funciona bem no PHP 5.3.

por exemplo. este exemplo (código obtido no formulário do feed do Google VirtueMart2)

for($n=0; $n<count($ids); $n++)
{
    //unset($product); //usefull for arrays
    $product = null
    if( $n % 50 == 0 )
    {
        // let GC do the memory job
        //echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
        time_nanosleep(0, 10000000);
    }

    $product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
    ...
OSP
fonte
3

Ainda duvido disso, mas tentei no meu script e estou usando o xdebug para saber como isso afetará o uso de memória do meu aplicativo. O script é definido na minha função assim:

function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
    $sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
    if($showSql === FALSE) {
        $sql = mysql_query($sql) or die(mysql_error());
        $data = mysql_fetch_array($sql);
        return $data[0];
    } else echo $sql;
}

E adiciono unset pouco antes do returncódigo e ele me dá: 160200, então eu tento alterá-lo $sql = NULLe ele me dá: 160224 :)

Mas há algo único nesse comparativo quando não estou usando unset () ou NULL, o xdebug me fornece 160144 como uso de memória

Então, acho que dar linha para usar unset () ou NULL adicionará processo ao seu aplicativo e será melhor manter a origem do seu código e diminuir a variável que você está usando o mais eficaz possível.

Me corrija se eu estiver errado, obrigado

Anggie Aziz
fonte
Acho que enquanto você retorna o item $ data [0], toda a matriz é referenciada / mas é apenas a hipótese. Tente copiar $ data [0] para a variável local, defina a matriz como nula e retorne a variável local. Um bom histórico é aqui tuxradar.com/practicalphp/18/1/11 e ofc. php.net/manual/en/features.gc.php
OSP
2

Eu criei um novo teste de desempenho para unsete =nullporque, como mencionado nos comentários, o aqui escrito tem um erro (a recriação dos elementos). Eu usei matrizes, como você vê, não importava agora.

<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = 'a';
    $arr2[$i] = 'a';
}

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = null;
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

Mas eu só posso testá-lo em um servidor PHP 5.5.9, aqui os resultados: - levou 4.4571571350098 segundos - levou 4.4425978660583 segundos

Eu prefiro unsetpor razões de legibilidade.

Michael B.
fonte
2

O PHP 7 já foi trabalhado nesses problemas de gerenciamento de memória e seu uso reduzido e mínimo.

<?php
  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
     $a = 'a';
     unset($a);
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

?>

Saída do PHP 7.1:

levou 0.16778993606567 segundos levou 0.16630101203918 segundos

Swapnil
fonte
1

unsetcódigo, se não liberar memória imediata, ainda é muito útil e seria uma boa prática fazer isso toda vez que passarmos as etapas do código antes de sairmos de um método. tome nota que não se trata de liberar memória imediata. memória imediata é para CPU, e quanto à memória secundária, que é RAM.

e isso também aborda a prevenção de vazamentos de memória.

consulte este link http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

Eu tenho usado unset por um longo tempo agora.

práticas recomendadas como essa no código para desabilitar instantaneamente todas as variáveis ​​que já foram usadas como matriz.

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

e just unset($data);liberar todo o uso variável.

consulte o tópico relacionado para desarmar

Quão importante é desabilitar variáveis ​​no PHP?

[erro]

zero8
fonte
1

Para o registro, e excluindo o tempo que leva:

<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n";

echo "<hr>function:<br>";
function test() {
    $x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

Retorna

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

Conclusão, memória livre nula e não configurada conforme o esperado (não apenas no final da execução). Além disso, a reatribuição de uma variável mantém o valor duas vezes em algum momento (520216 versus 438352)

Magallanes
fonte