Erro fatal: tamanho de memória permitido de 134217728 bytes esgotados (CodeIgniter + XML-RPC)

618

Eu tenho vários sistemas de ponto de venda (POS) do cliente que enviam periodicamente novos dados de vendas para um banco de dados centralizado, que armazena os dados em um grande banco de dados para geração de relatórios.

O POS do cliente é baseado no PHPPOS, e eu implementei um módulo que usa a biblioteca XML-RPC padrão para enviar dados de vendas ao serviço. O sistema do servidor é construído no CodeIgniter e usa as bibliotecas XML-RPC e XML-RPCS para o componente de serviço da web. Sempre que envio muitos dados de vendas (apenas 50 linhas da tabela de vendas e linhas individuais de sales_items pertencentes a cada item da venda), recebo o seguinte erro:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 54 bytes)

128M é o valor padrão php.ini, mas presumo que seja um número enorme para quebrar. Na verdade, eu até tentei definir esse valor para 1024M, e tudo o que faz é levar mais tempo para ocorrer um erro.

Quanto às etapas que eu tomei, tentei desabilitar todo o processamento no lado do servidor e o montei para retornar uma resposta fixa, independentemente da entrada. No entanto, acredito que o problema está no envio real dos dados. Eu até tentei desabilitar o tempo máximo de execução de script para PHP, e ele ainda gera erros.

ArcticZero
fonte
5
Estou um pouco confuso ... onde ocorre o erro - no cliente ou servidor? E em que estágio ... cliente enviando, recebendo servidor, processando servidor, enviando servidor, recebendo cliente ou processando cliente?
Greg
2
O erro parece ocorrer durante o envio do cliente ou o recebimento do servidor. Tentei desabilitar todo o processamento no servidor e manipulá-lo para enviar uma resposta fixa, independentemente dos dados enviados. O erro ocorre se eu enviar uma certa quantidade de dados. Estou alterando a configuração do PHP.ini.
21420 ArcticZero
42
O limite de memória é de 128 MB, tente:ini_set('memory_limit', '256M');
9
O resumo votou negativamente em todas as respostas "apenas ignore o vazamento", pessoas que confundiram o CodeIgniter com Drupal e pessoas que apenas copiaram e colaram as respostas de outras pessoas para obter pontos. A qualidade das respostas neste é abismal.
Matti Virkkunen

Respostas:

697

Alterar o memory_limitby nãoini_set('memory_limit', '-1'); é uma solução adequada. Por favor, não faça isso.

Seu código PHP pode ter um vazamento de memória em algum lugar e você está dizendo ao servidor para usar apenas toda a memória que ele deseja. Você não teria resolvido o problema. Se você monitorar seu servidor, verá que agora está provavelmente usando a maior parte da RAM e até trocando para o disco.

Você provavelmente deve tentar rastrear o código incorreto no seu código e corrigi-lo.

Jeff
fonte
174
@ Jeff, você provavelmente está certo 95% do tempo. No entanto, há momentos em que você realmente precisa de mais memória. Por exemplo, digamos que seu aplicativo esteja carregando uma quantidade enorme de dados na memória para processamento (por exemplo, uma lista de materiais com 15 mil componentes). Não é sempre que o código está com erros, às vezes você só precisa de um pouco mais de memória (por exemplo, 256M em vez de 128M). No entanto, concordo que defini-lo como -1 é terrivelmente ruim. Mas ajustar o limite de memória para situações razoáveis ​​em tempo de execução é perfeitamente aceitável.
Pirita
24
@pyrite sim você está certo que às vezes um processo requer mais memória, mas você deve aumentar o limite de memória para uma certa quantidade lógica como 256MB como você disse ou 512MB por que não, mas não 1;)
Lukas Lukac
9
@jeff Concordo plenamente, um valor de -1poderia ser útil apenas em ambientes de desenvolvimento para fins de teste.
Esolitos 03/03
4
@Pyrite nos casos que você nomeou para os 5% restantes leem os dados em pedaços e usam um trabalhador para processá-los, em vez de usar mais memória. Essa solução também será dimensionada, enquanto sua sugestão não funcionará, exceto se você coloca mais e mais memória no servidor ao longo do tempo, se os dados aumentarem.
burzum
2
Da maneira mais comum, esse problema no ORM quando você tenta buscar todos os dados com muito mais limite de memória php. Por exemplo, quando você tenta gerar um relatório mensal.
Stepchik
213

ini_set('memory_limit', '-1');substitui o limite de memória PHP padrão .

Chris Lane
fonte
16
@williamcarswell; -1é um valor que o PHP entende como ilimitado neste contexto.
Alix Axel
7
@ ArseniuszŁozicki - também consumirá recursos que o servidor não pode poupar.
Ken Williams
124
Pena que isso receba tantos votos positivos. Configurá-lo para um valor preciso, com edições do php.ini ou ini_set, é uma solução perfeitamente válida quando as pessoas precisam de mais memória. Defini-lo para ilimitado é um hack perigoso :(
Jeff Davis
24
@ user1767586 e defina-o como um valor sensato. Você pode impedir que o script gere o erro configurando-o para 1024M. Se esta resposta disser ini_set ('memory_limit', '1024M'); Você pode copiar e colar isso e ficar bem. Ao configurá-lo como -1, você está configurando um script que consome toda a memória. Especialmente se você fizer isso rotineiramente. Colocar "perigoso" entre aspas não o torna menos perigoso. Você realmente pode usar o seu servidor host. Talvez comece a destruir dados. Não sei, talvez perca o emprego? Parece muito perigoso para mim. : |
Jeff Davis
3
É triste ver que a resposta para +161 votos e -3 votos é a mesma :(
akarthik10
130

A maneira correta é editar seu php.iniarquivo. Edite memory_limitpara o seu valor desejado.

A partir da sua pergunta 128M(que é o limite padrão) foi excedida, há algo seriamente errado no seu código, pois não deve demorar muito.

Se você sabe por que é preciso muito e deseja permitir que o valor seja definido memory_limit = 512Mou superior, você deve ser bom.

Basav
fonte
7
Honestamente, se você estiver armazenando em cache algumas quantidades sérias de dados, esta é a resposta correta. 128M não é suficiente para certos scripts. 512M ou 1024M costumam ser suficientes, mas você precisa decidir caso a caso.
Jeff Davis
2
Yeha, no entanto tentar evitar o uso de memória enorme, se o número de usuários estão indo para ser mais
Basav
2
limite_de memória = -1; O que é o php.ini?
2
@YumYumYum Isso remove o limite de memória, que você só quer se estiver monitorando o uso da memória de outra maneira. O sistema operacional interromperá o processo se estiver consumindo uma quantidade enorme de memória em algum momento.
Flimm
Portanto, se você estiver executando um script que utiliza muita memória, mas precisa executá-lo apenas uma vez, basta aumentar o limite de memória do processo no momento da execução e diminuir o seu limite de memória novamente após o uso único script é executado?
chromechris 16/02
95

A alocação de memória para PHP pode ser ajustada permanentemente ou temporariamente.

Permanentemente

Você pode alterar permanentemente a alocação de memória PHP de duas maneiras.

Se você tiver acesso ao seu php.iniarquivo, poderá editar o valor para memory_limito valor desejado.

Se você não tiver acesso ao seu php.iniarquivo (e o seu host permitir), poderá substituir a alocação de memória pelo seu .htaccessarquivo. Adicione php_value memory_limit 128M(ou seja qual for a alocação desejada).

Temporário

Você pode ajustar a alocação de memória em tempo real a partir de um arquivo PHP. Você simplesmente tem o código ini_set('memory_limit', '128M');(ou qualquer que seja a alocação desejada). Você pode remover o limite de memória (embora os limites da máquina ou da instância ainda possam se aplicar), definindo o valor como "-1".

Umair Idrees
fonte
2
Obrigado Eu não acho que para verificar se alguém tinha definir o valor no .htaccess que foi substituindo php.ini e eu não conseguia descobrir por que +1
HostMyBus
61

É muito fácil obter vazamentos de memória em um script PHP - especialmente se você usar abstração, como um ORM. Tente usar o Xdebug para criar um perfil do seu script e descobrir para onde foi toda essa memória.

Troelskn
fonte
1
Vou tentar o Xdebug. Eu nunca o usei antes, então terei que ler sobre isso. Obrigado por responder! Espero que eu encontrar a resposta para isso em breve ...
ArcticZero
34
Lembre-se de que o PHP usa contagem de referência para gerenciar memória. Portanto, se você tiver referências circulares ou variáveis ​​globais, esses objetos não serão reciclados. Isso geralmente é a raiz do vazamento de memória no PHP.
troelskn
O Xdebug mostra que a biblioteca Xmlrpc.php da CI é responsável pelo vazamento de memória. Por acaso, haveria algum problema com as bibliotecas XML-RPC do CodeIgniter que eu deveria conhecer? Tentei desabilitar todo o processamento do lado do servidor e ele ainda ficará sem memória se eu fornecer dados suficientes.
21420 ArcticZero
1
Não conheço / uso o IC, então não sei. Mas você provavelmente deve tentar encontrar um objeto que não seja liberado após o uso - provavelmente devido a uma referência cíclica. É trabalho de detetive.
troelskn
1
Esta é a única resposta aqui que aconselha realmente abordar o problema. As outras respostas aumentam a memória para curar um sintoma e ignorar a doença .
Chris Baker
56

Ao adicionar 22,5 milhões de registros em uma matriz com array_push, eu continuava recebendo erros fatais de "memória esgotada" em cerca de 20 milhões de registros usando 4Gcomo limite de memória no arquivo php.ini. Para corrigir isso, adicionei a declaração

$old = ini_set('memory_limit', '8192M');

na parte superior do arquivo. Agora tudo está funcionando bem. Eu não sei se o PHP tem um vazamento de memória. Esse não é o meu trabalho, nem me importo. Eu só tenho que fazer meu trabalho, e isso funcionou.

O programa é muito simples:

$fh = fopen($myfile);
while (!feof($fh)) {
    array_push($file, stripslashes(fgets($fh)));
}
fclose($fh);

O erro fatal aponta para a linha 3 até eu aumentar o limite de memória, o que eliminou o erro.

JamesAD-0
fonte
10
você quer dizer ini_set('memory_limit', '8192M');?
Gogol
2
Que luxo seria ter tempo para otimizar um script para algo assim. Ou pesquise, compare e aprenda ferramentas ETL ou algo parecido. No mundo real, aumentamos a quantidade de memória, fazemos a coisa e seguimos em frente.
Matthew Poer
45

Eu continuava recebendo esse erro, mesmo com memory_limitset in php.ini, e o valor sendo lido corretamente com phpinfo().

Alterando-o a partir disso:

memory_limit=4G

Para isso:

memory_limit=4096M

Isso corrigiu o problema no PHP 7.

Danny Beckett
fonte
23

Quando você vê o erro acima - especialmente se o (tried to allocate __ bytes)valor é baixo, isso pode ser um indicador de um loop infinito, como uma função que se chama sem saída:

function exhaustYourBytes()
{
    return exhaustYourBytes();
}
Kristen Waite
fonte
19

Depois de ativar essas duas linhas, ele começou a funcionar:

; Determines the size of the realpath cache to be used by PHP. This value should
; be increased on systems where PHP opens many files to reflect the quantity of
; the file operations performed.
; http://php.net/realpath-cache-size
realpath_cache_size = 16k

; Duration of time, in seconds for which to cache realpath information for a given
; file or directory. For systems with rarely changing files, consider increasing this
; value.
; http://php.net/realpath-cache-ttl
realpath_cache_ttl = 120

Prem Kumar Maurya
fonte
19

Você pode corrigir isso corretamente, alterando o memory_limitfastcgi / fpm:

$vim /etc/php5/fpm/php.ini

Mude a memória, como de 128 para 512, veja abaixo

; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 128M

para

; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 512M
Derick Fynn
fonte
18

Diretório raiz do seu site:

ini_set('memory_limit', '1024M');
Gaurang P
fonte
1
isso funcionou para mim. amo soluções de uma linha. +1 para simplificar
Steve C
14

Altere o limite de memória no arquivo php.ini e reinicie o Apache. Após o reinício, execute o phpinfo (); função de qualquer arquivo PHP para uma memory_limitconfirmação de alteração.

memory_limit = -1

Limite de memória -1 significa que não há limite de memória definido. Agora está no máximo.

Hasib Kamal
fonte
13

Para usuários do Drupal, esta resposta de Chris Lane:

ini_set('memory_limit', '-1');

funciona, mas precisamos colocá-lo logo após a abertura

<?php

no arquivo index.php no diretório raiz do seu site.

sigmapi13
fonte
13

No Drupal 7, você pode modificar o limite de memória no arquivo settings.php localizado na sua pasta sites / padrão. Na linha 260, você verá o seguinte:

ini_set('memory_limit', '128M');

Mesmo que suas configurações de php.ini sejam altas o suficiente, você não poderá consumir mais de 128 MB se isso não estiver definido no seu arquivo Drupal settings.php.

LK7889
fonte
1
Não em Drupal7 não existe essa seqüência de código em settings.php
FLY
Também não há cadeia na settings.php para Drupal 6
AllisonC
12

Em vez de alterar o memory_limitvalor do seu php.iniarquivo, se houver uma parte do seu código que possa usar muita memória, você poderá remover o memory_limitantes dessa seção e substituí-lo depois.

$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
mykeels
fonte
7

O PHP 5.3+ permite alterar o limite de memória colocando um .user.iniarquivo na public_htmlpasta Simplesmente crie o arquivo acima e digite a seguinte linha:

memory_limit = 64M

Alguns hosts cPanel aceitam apenas esse método.

Sabi
fonte
7

Bater página?

Digite a descrição da imagem aqui

(Isso acontece quando o MySQL precisa consultar linhas grandes. Por padrão, memory_limit é definido como pequeno, o que era mais seguro para o hardware.)

Você pode verificar o status da memória existente do sistema antes de aumentar php.ini:

# free -m
             total       used       free     shared    buffers     cached
Mem:         64457      63791        666          0       1118      18273
-/+ buffers/cache:      44398      20058
Swap:         1021          0       1021

Aqui eu a ampliei como a seguir e depois o faço service httpd restartpara corrigir o problema da página de falha.

# grep memory_limit /etc/php.ini
memory_limit = 512M
Peter Mortensen
fonte
Qual número (linha e coluna?) Deve-se observar após executar o free -mcomando para decidir sobre um novo limite de memória?
kiradotee 23/01
7

Basta adicionar uma ini_set('memory_limit', '-1');linha na parte superior da sua página da web.

E você pode definir sua memória conforme sua necessidade no lugar de -1, para 16M, etc.

Pankaj Pratik Rai
fonte
6
Isso parece dizer a mesma coisa que muitas respostas existentes. É melhor adicionar uma resposta a uma pergunta popular apenas se o novo material oferecer algo novo.
Halfer 10/05/19
6

Para aqueles que estão coçando a cabeça para descobrir por que diabos essa pequena função deve causar um vazamento de memória, às vezes por um pequeno erro, uma função começa a se chamar recursivamente para sempre.

Por exemplo, uma classe de proxy que tem o mesmo nome para uma função do objeto que o fará proxy.

class Proxy {

    private $actualObject;

    public function doSomething() {

        return $this->actualObjec->doSomething();
    }
}

Às vezes, você pode esquecer de trazer esse pequeno membro realObjec e, como o proxy realmente possui esse doSomethingmétodo, o PHP não forneceria nenhum erro e, para uma classe grande, ele poderia ficar oculto por alguns minutos para descobrir por que está vazando a memória.

madz
fonte
E outra dica: você pode inserir die('here')seu código e mover essa declaração para ver onde a recursão começa.
Toddmo # 6/18
6

Ocorreu o erro abaixo ao executar em um conjunto de dados menor do que o que funcionava anteriormente.

Erro fatal: tamanho de memória permitido de 134217728 bytes esgotado (tentou alocar 4096 bytes) em C: \ workspace \ image_management.php na linha 173

Como a busca pela falha me trouxe aqui, pensei em mencionar que nem sempre são as soluções técnicas nas respostas anteriores, mas algo mais simples. No meu caso, foi o Firefox. Antes de executar o programa, ele já estava usando 1.157 MB.

Acontece que eu estava assistindo um vídeo de 50 minutos, um pouco de cada vez, durante um período de dias, e isso estragou tudo. É o tipo de correção que os especialistas corrigem sem nem pensar, mas para mim vale a pena lembrar.

M61Vulcan
fonte
Eu tive uma ocorrência semelhante no Google Chrome hoje. Fiquei extremamente cético em relação a essa resposta ... no entanto, ela revelou que minha exaustão de bytes desapareceu depois que abri uma janela anônima e disparei o mesmo script novamente! A pesquisa continua.
mickmackusa 22/04
2

A execução do script como este (caso cron, por exemplo): php5 /pathToScript/info.phpproduz o mesmo erro.

A maneira correta: php5 -cli /pathToScript/info.php

ratm
fonte
2

Se você estiver executando um VPS (servidor privado virtual) com WHM, poderá descobrir que não possui permissões para editar o PHP.INI diretamente; o sistema deve fazer isso. No painel de controle do host do WHM, vá para Service ConfigurationPHP Configuration Editor e modifique memory_limit:

Atualizando memory_limit no WHM 11.48.4

Janckos
fonte
2

Acho útil quando incluir ou exigir _dbconnection.php_e _functions.phpnos arquivos realmente processados, em vez de incluir no cabeçalho. O que está incluído em si.

Portanto, se o cabeçalho e o rodapé estiverem incluídos, basta incluir todos os seus arquivos funcionais antes da inclusão do cabeçalho.

Kerim
fonte
2

Usar yieldpode ser uma solução também. Consulte Sintaxe do gerador .

Em vez de alterar o PHP.iniarquivo para um armazenamento de memória maior, às vezes a implementação de yieldum loop interno pode corrigir o problema. O que o rendimento faz é, em vez de descarregar todos os dados de uma vez, ele os lê um por um, economizando muito uso de memória.

LukeDS
fonte
2
PHP.ini? Não é php.ini?
Peter Mortensen
1

Às vezes, esse erro é causado por um bug no código PHP que causa recursões envolvendo manipulação de exceções e possivelmente outras operações. Infelizmente, não consegui criar um pequeno exemplo.

Nestes casos, o que aconteceu várias vezes, set_time_limitfalha e o navegador continua tentando carregar a saída do PHP, com um loop infinito ou com a mensagem de erro fatal que é o tópico desta pergunta.

Reduzindo o tamanho de alocação permitido adicionando

ini_set('memory_limit','1M');

próximo ao início do seu código, você poderá evitar o erro fatal.

Em seguida, você pode ficar com um programa que termina, mas ainda é difícil de depurar.

Nesse ponto, insira BreakLoop()chamadas dentro do seu programa para obter controle e descobrir qual loop ou recursão no seu programa está causando o problema.

A definição de BreakLoop é a seguinte:

function BreakLoop($MaxRepetitions=500,$LoopSite="unspecified")
    {
    static $Sites=[];
    if (!@$Sites[$LoopSite] || !$MaxRepetitions)
        $Sites[$LoopSite]=['n'=>0, 'if'=>0];
    if (!$MaxRepetitions)
        return;
    if (++$Sites[$LoopSite]['n'] >= $MaxRepetitions)
        {
        $S=debug_backtrace(); // array_reverse
        $info=$S[0];
        $File=$info['file'];
        $Line=$info['line'];
        exit("*** Loop for site $LoopSite was interrupted after $MaxRepetitions repetitions. In file $File at line $Line.");
        }
    } // BreakLoop

O argumento $ LoopSite pode ser o nome de uma função no seu código. Não é realmente necessário, pois a mensagem de erro que você receberá apontará para a linha que contém a chamada BreakLoop ().

David Spector
fonte
-1

No meu caso, foi um breve problema com a forma como uma função foi escrita. Um vazamento de memória pode ser causado pela atribuição de um novo valor à variável de entrada de uma função, por exemplo:

/**
* Memory leak function that illustrates unintentional bad code
* @param $variable - input function that will be assigned a new value
* @return null
**/
function doSomehting($variable){
    $variable = 'set value';
    // Or
    $variable .= 'set value';
}
Dmitriy Kravchuk
fonte
-6

Quando removi as seguintes linhas do meu código, tudo funcionou bem!

set_include_path(get_include_path() . get_include_path() . '/phpseclib');
include_once('Net/SSH2.php');
include_once('Net/SFTP.php');

Essas linhas foram incluídas em todos os arquivos que eu estava executando. Ao executar os arquivos um por um, tudo funcionou bem, mas ao executar todos os arquivos juntos, tive o problema de vazamento de memória. De alguma forma, o "include_once" não está incluindo as coisas uma vez, ou estou fazendo algo errado ...

Omar Al-Azzawi
fonte
set_include_path(get_include_path() . get_include_path().'/phpseclib'); Isso adicionará o caminho '/ phpseclib' uma vez para cada arquivo que tem a linha ... para que ele possa ser adicionado várias vezes! Eu sugiro colocá-lo em um arquivo de configurações e include_onceo arquivo de configurações.
Farfromunique