O que é a fragmentação da memória?

Ouvi o termo "fragmentação de memória" usado algumas vezes no contexto da alocação dinâmica de memória C++. Eu encontrei algumas perguntas sobre como lidar com a fragmentação da memória, mas não consigo encontrar uma pergunta direta que lida com ela mesma. Então ...

    O que é a fragmentação da memória? Como posso saber se a fragmentação da memória é um problema para a minha aplicação? Que tipo de programa é mais provável sofrer? Quais são as boas maneiras comuns de lidar com a memória? fragmentação?

também:

    Ouvi dizer que usar alocações dinâmicas pode aumentar a fragmentação da memória. É verdade? No contexto de C++, eu entendo todos os contêineres padrão (std: string, std:: vector, etc) usam alocação dinâmica de memória. Se estes são usados ao longo de um programa (especialmente std: string), é mais provável que a fragmentação da memória seja um problema? Como é que a fragmentação da memória pode ser tratada numa aplicação pesada?
Author: AshleysBrain, 2010-09-22

11 answers

Imagine que você tem uma extensão" grande " (32 bytes) de memória Livre:

----------------------------------
|                                |
----------------------------------

Agora, atribua alguns deles (5 atribuições):

----------------------------------
|aaaabbccccccddeeee              |
----------------------------------
Agora, liberte os primeiros quatro créditos, mas não o quinto.
----------------------------------
|              eeee              |
----------------------------------
Agora, tenta alocar 16 bytes. Não posso, apesar de haver quase o dobro de graça. Em sistemas com memória virtual, a fragmentação é menos um problema do que se possa pensar, porque as grandes atribuições só têm de ser contíguas em virtual espaço de endereços, não emfísico espaço de endereços. Assim, no meu exemplo, se eu tivesse memória virtual com um tamanho de página de 2 bytes, então eu poderia fazer minha alocação de 16 bytes sem problema. A memória física seria assim:
----------------------------------
|ffffffffffffffeeeeff            |
----------------------------------

Considerando que a memória virtual (sendo muito maior) pode parecer assim:

------------------------------------------------------...
|              eeeeffffffffffffffff                   
------------------------------------------------------...
O sintoma clássico da fragmentação da memória é que se tenta alocar um bloco grande e não se pode, mesmo que pareça ter memória suficiente livre. Outra consequência possível é a incapacidade do processo para liberar a memória de volta para o SO (porque há algum objeto ainda em uso em todos os blocos que ele alocou a partir do SO, mesmo que esses blocos são agora principalmente não utilizados).

Tácticas para evitar a fragmentação da memória no trabalho em C++, atribuindo objectos de diferentes áreas de acordo com o seu tamanho e/ou a sua vida útil esperada. Então, se você vai criar um monte de objetos e destruí - los todos juntos mais tarde, alocá-los de um piscina de memória. Qualquer outro alocamento que você faça entre eles não será da piscina, portanto, não será localizado entre eles na memória, então a memória não será fragmentada como resultado.

Geralmente você não precisa se preocupar muito com isso, a menos que seu programa esteja em execução longa e faça muita alocação e libertação. É quando você tem misturas de objetos de curta e longa duração que você está mais em risco, mas mesmo assim malloc vai fazer o seu melhor para ajudar. Basicamente, ignore-o até o seu programa ter falhas de alocação ou inesperadamente faz com que o sistema fique com pouca memória (pegue isso no teste, por preferência!).

As bibliotecas padrão não são piores do que qualquer outra coisa que aloca a memória, e os recipientes padrão todos têm um parâmetro de modelo Alloc que você poderia usar para afinar a sua estratégia de alocação, se absolutamente necessário.

 249
Author: Steve Jessop, 2011-09-08 00:31:06

O que é a fragmentação da memória?

A fragmentação da memória é quando a maior parte da memória é alocada num grande número de blocos não contíguos, ou pedaços-deixando uma boa percentagem da memória total não alocada, mas inutilizável para os cenários mais típicos. Isto resulta em exceções de memória ou erros de alocação (ou seja, malloc retorna nulo).

A maneira mais fácil de pensar nisto é imaginar que tens uma grande parede vazia que precisas de pôr imagens de diferentes tamanhos ligadas. Cada imagem ocupa um determinado tamanho e você obviamente não pode dividi-lo em pedaços menores para fazê-lo caber. Você precisa de um lugar vazio na parede, o tamanho da imagem, ou então você não pode colocá-lo para cima. Agora, se você começar a pendurar fotos na parede e você não é cuidadoso sobre como você organizá-los, você vai acabar em breve com uma parede que está parcialmente coberto de imagens e mesmo que você pode ter pontos vazios a maioria das fotos novas não vai caber porque eles são maior do que os lugares disponíveis. Você ainda pode pendurar fotos muito pequenas, mas a maioria não vai caber. Então você terá que re-organizar (compacto) os já na parede para dar espaço para mais.. Agora, imagine que a parede é a sua memória (heap) e as imagens são objetos.. Isso é fragmentação de memória..

Como posso saber se a fragmentação da memória é um problema para a minha aplicação? Que tipo de programa é mais provável sofrer?

Um sinal revelador de que pode ser lidar com a fragmentação da memória é se você recebe muitos erros de alocação, especialmente quando a porcentagem de memória usada é alta - mas não você ainda não usou toda a memória - então tecnicamente você deve ter muito espaço para os objetos que você está tentando alocar.

Quando a memória está fortemente fragmentada, as alocações de memória provavelmente demorarão mais porque o alocador de memória tem que fazer mais trabalho para encontrar um espaço adequado para o novo objeto. Se por sua vez você tem muitas atribuições de memória (o que você provavelmente faz desde que você acabou com a fragmentação da memória) o tempo de alocação pode até mesmo causar atrasos perceptíveis.

Quais são as boas maneiras comuns de lidar com a fragmentação da memória?

Usa um bom algoritmo para distribuir memória. Em vez de atribuir memória para um monte de objetos pequenos, pré-alocar memória para uma matriz contígua desses objetos menores. Às vezes, ser um pouco desperdiçador quando a alocação de memória pode ir ao longo do caminho para o desempenho e pode salvar-lhe o dificuldade em lidar com a fragmentação da memória.

 67
Author: Mike Dinescu, 2010-09-22 15:08:36

A fragmentação da memória é o mesmo conceito que a fragmentação do disco: refere-se ao espaço desperdiçado porque as áreas em uso não são embaladas suficientemente juntas.

Suponha por um exemplo simples de brinquedo que você tem dez bytes de memória:
 |   |   |   |   |   |   |   |   |   |   |
   0   1   2   3   4   5   6   7   8   9
Agora vamos alocar três blocos de três bytes, Nome A, B E C:
 | A | A | A | B | B | B | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9

Agora desallocate Bloco B:

 | A | A | A |   |   |   | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9
O que acontece se tentarmos alocar um bloco D De quatro bytes? Bem, temos quatro bytes livres de memória, mas ... nós não temos quatro bytes contíguos de memória Livre, então não podemos alocar D! Trata-se de uma utilização ineficiente da memória, porque devíamos ter conseguido armazenar D, mas não conseguimos. E nós não podemos mover C para criar espaço, porque muito provavelmente algumas variáveis em nosso Programa estão apontando para C, e nós não podemos encontrar e mudar automaticamente todos esses valores. Como sabes que é um problema? Bem, o maior sinal é que o tamanho da memória virtual do seu programa é consideravelmente maior do que o quantidade de memória que estás a usar. Em um exemplo do mundo real, você teria muito mais do que dez bytes de memória, então D apenas seria alocado começando um byte 9, e bytes 3-5 permaneceria sem uso a menos que você mais tarde alocou algo três bytes de comprimento ou menor.

Neste exemplo, 3 bytes não é muito para desperdiçar, mas considere um caso mais patológico onde duas alocações de um par de bytes são, por exemplo, dez megabytes separados em memória, e você precisa alocar um bloco de Tamanho 10 megabytes + 1 byte. Você tem que ir pedir ao SO mais de dez megabytes de memória virtual para fazer isso, mesmo que você está apenas a um byte de ter espaço suficiente já.

Como se evita isso? Os piores casos tendem a surgir quando você frequentemente cria e destrói pequenos objetos, uma vez que isso tende a produzir um efeito "queijo suíço" com muitos pequenos objetos separados por muitos pequenos buracos, tornando impossível alocar objetos maiores nesses buracos. Quando você sabe que vai ao fazer isso, uma estratégia eficaz é pré-alocar um grande bloco de memória como um pool para seus pequenos objetos, e, em seguida, gerir manualmente a criação dos pequenos objetos dentro desse bloco, em vez de deixar o alocador padrão lidar com ele. Em geral, quanto menos alocações você faz, menos provável é a memória ficar fragmentada. No entanto, STL lida com isso de forma bastante eficaz. Se tiver um texto que está a usar a totalidade da sua atribuição actual e adicionar um carácter a it, it doesn't simply re-allocate to its current length plus one, it doubles its length. Trata-se de uma variação da estratégia "pool for frequent small allocations". A corda está pegando um grande pedaço de memória para que possa lidar de forma eficiente com repetidos pequenos aumentos de tamanho sem fazer repetidos reatribuições pequenas. Todos os containers STL de fato fazem esse tipo de coisa, então geralmente você não precisa se preocupar muito com a fragmentação causada por STL de reatribuição automática cisterna.

, Embora, claro, contentores STL não memória de pool entre uns aos outros, então se você vai criar muitos pequenos contêineres (em vez de alguns recipientes que obter redimensionados com freqüência) você pode ter que se preocupar com a prevenção de fragmentação da mesma forma que você faria para qualquer frequentemente criados pequenos objetos, STL ou não.

 22
Author: Tyler McHenry, 2010-09-22 15:10:20
    O que é a fragmentação da memória?

A fragmentação da memória é o problema de a memória se tornar inutilizável mesmo que esteja teoricamente disponível. Existem dois tipos de fragmentação: fragmentação interna é a memória é alocada, mas não pode ser usado (por exemplo, quando a memória é alocada em blocos de 8 bytes, mas o programa repetidamente faz único allications quando ele precisa apenas 4 bytes). fragmentação externa é o problema da memória livre tornando-se dividido em muitos pedaços pequenos de modo que os pedidos de alocação grandes não podem ser atendidos embora haja memória livre suficiente.

    Como posso saber se a fragmentação da memória é um problema para a minha aplicação? Que tipo de programa é mais provável sofrer?

A fragmentação da memória é um problema se o seu programa usa muito mais memória do sistema do que os seus dados paylod reais exigiriam (e você descartou fugas de memória).

  • Quais são boas maneiras comuns de lidar com a fragmentação da memória?
Use um bom alocador de memória. IIRC, aqueles que usam uma estratégia de "melhor ajuste" são geralmente muito superiores em evitar a fragmentação, se um pouco mais lento. No entanto, ficou também demonstrado que, para qualquer estratégia de atribuição, existem casos patológicos piores. Felizmente, os padrões típicos de alocação da maioria das aplicações são realmente relativamente benignos para os alocadores para lidar. Há um monte de papéis lá fora se você é interessado nos detalhes:
    Paul R. Wilson, Mark S. Johnstone, Michael Neely e David Boles. Dynamic Storage Allocation: A Survey and Critical Review. Nos trabalhos de 1995 Seminário Internacional sobre Gestão da memória, Springer Verlag LNCS, 1995 Mark S. Johnstone, Paul R. Wilson. O Problema De Fragmentação Da Memória: Resolvido? In ACM SIG-PLAN Notices, volume 34 No. 3, pages 26-36, 1999
  • M. R. Garey, R. L. Graham e J. D. Ullman. Análise da memória no pior dos casos algoritmos de alocação. In Fourth Annual ACM Symposium on the Theory of Computing, 1972
 14
Author: Michael Borgwardt, 2017-03-17 10:09:21

Actualizar:
Google TCMalloc: Thread-Caching Malloc
Verificou-se que é bastante bom a lidar com a fragmentação num processo a longo prazo.


Tenho desenvolvido uma aplicação de servidor que teve problemas com a fragmentação da memória em HP-UX 11.23/11.31 ia64.

Parecia isto. Houve um processo que fez alocações de memória e deallocações e funcionou por dias. E mesmo que houvesse nenhuma memória vaza consumo de memória do processo continuou a aumentar. Sobre a minha experiência. Em HP-UX é muito fácil encontrar fragmentação de memória usando gdb HP-UX. Você define um ponto de paragem e quando o carrega, executa este comando: info heap e vê todas as atribuições de memória para o processo e o tamanho total do heap. Em seguida, o seu Continuar o seu programa e, em seguida, algum tempo mais tarde, o seu novamente atingiu o ponto de ruptura. Fazes outra vez. Se o tamanho total de heap é maior, mas o número e o tamanho de alocações separadas são as mesmas, então é provável que você tenha problemas de alocação de memória. Se necessário, verifique algumas vezes. A minha maneira de melhorar a situação era esta. Depois que eu tinha feito alguma análise com o gdb HP-UX eu vi que problemas de memória foram causados pelo fato de que eu usei std::vector para armazenar alguns tipos de informação de uma base de dados. std::vector exige que os seus dados sejam guardados num bloco. Eu tinha alguns contentores baseados em std::vector. Estes contentores foram regularmente recriar. Houve muitas vezes situações em que novos registros foram adicionados à base de dados e depois que os contêineres foram recriados. E como os recipientes recriados eram maiores, eles não se encaixavam em blocos disponíveis de memória livre e o tempo de execução pediu um novo bloco maior do so. Como resultado, apesar de não haver vazamentos de memória, O consumo de memória do processo cresceu. Melhorei a situação quando mudei os contentores. Em vez de std::vector comecei a usar std::deque que tem um forma diferente de distribuir memória por dados.

Eu sei que uma das maneiras de evitar a fragmentação da memória em HP-UX é usar o alocador de blocos pequenos ou usar MallocNextGen. No RedHat Linux, o alocador padrão parece lidar muito bem com a alocação de um monte de pequenos blocos. Nas janelas há Low-fragmentation Heap e adere ao problema de um grande número de pequenas alocações.

O meu entendimento é que, numa aplicação pesada de STL, temos Primeiro de identificar problemas. Alocadores de memória (como na libc) realmente lidar com o problema de um monte de alocações pequenas, o que é típico para std::string (por exemplo, na minha aplicação de servidor há muitos STL strings, mas como eu vejo a partir de execução {[[0]} eles não estão causando quaisquer problemas). A minha impressão é que é necessário evitar grandes afectações frequentes. Infelizmente, há situações em que você não pode evitá-los e tem que mudar o seu código. Como digo no meu caso, melhorei a situação quando mudei para std::deque. Se identificar a sua memória fragmentação pode ser possível falar sobre isso com mais precisão.
 9
Author: Sergei Kurenkov, 2011-04-08 06:41:20

A fragmentação da memória é mais provável de ocorrer quando você alocar e desallocate muitos objetos de tamanhos variados. Suponha que você tem o seguinte layout na memória:

obj1 (10kb) | obj2(20kb) | obj3(5kb) | unused space (100kb)
Agora, quando obj2 é lançado, você tem 120kb de memória não utilizada, mas você não pode alocar um bloco completo de 120kb, porque a memória está fragmentada.

As técnicas comuns para evitar esse efeito incluem tampões de anel e conjuntos de objectos. No contexto do STL, métodos como std::vector::reserve() pode ajudar.

 6
Author: Björn Pollex, 2011-06-17 06:19:12
Uma resposta muito detalhada sobre a fragmentação da memória pode ser encontrada aqui.

Http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/

Este é o culminar de 11 anos de respostas de fragmentação de memória que tenho fornecido às pessoas que me fazem perguntas sobre fragmentação de memória em softwareverify.com
 6
Author: Stephen Kellett, 2015-10-12 23:25:45
O que é a fragmentação da memória?
Quando o seu aplicativo usa memória dinâmica, ele aloca e liberta pedaços de memória. No início, todo o espaço de memória do seu aplicativo é um bloco contíguo de memória livre. No entanto, quando você alocar e liberar blocos de tamanho diferente, a memória começa a ficar fragmentada, ou seja, em vez de um grande bloco contíguo livre e um número de blocos contíguos alocados, haverá um alocado e livre blocos misturados. Desde a blocos livres têm tamanho limitado, é difícil reutilizá-los. Por exemplo, você pode ter 1000 bytes de memória livre, mas não pode alocar memória para um bloco de 100 bytes, porque todos os blocos livres têm no máximo 50 bytes de comprimento.

Outra fonte inevitável, mas menos problemática de fragmentação é que na maioria das arquiteturas, endereços de memória devem ser alinhados para 2, 4, 8 etc. limites de byte(isto é, os endereços devem ser múltiplos de 2, 4, 8 etc.) Isto significa que mesmo que você tenha, por exemplo, uma estrutura contendo 3 {[[0]} campos, a sua estrutura pode ter um tamanho de 12 em vez de 3, devido ao facto de cada campo estar alinhado com um limite de 4 bytes.

Como posso saber se a fragmentação da memória é um problema para a minha aplicação? Que tipo de programa é mais provável sofrer?

A resposta óbvia é que você tem uma excepção fora da memória.

Aparentemente não há uma boa maneira portátil de detectar fragmentação de memória em aplicações c++. Veja esta resposta para mais informacao.

Quais são as boas maneiras comuns de lidar com a fragmentação da memória?

É difícil em C++, uma vez que você usa endereços de memória direta em ponteiros, e você não tem controle sobre quem faz referência a um endereço de memória específico. Assim, reorganizar os blocos de memória alocados (a forma como o coletor de lixo Java faz) não é uma opção.

Um alocador personalizado pode ajudar gerindo a alocação de pequenos objetos em um pedaço maior de memória, e reutilizando as fendas livres dentro daquele pedaço.

 3
Author: Péter Török, 2017-05-23 11:47:32
Esta é uma versão super simplificada para bonecos. À medida que os objetos são criados na memória, eles são adicionados ao final da porção usada na memória.

Se um objeto que não está no final da porção de memória usada é excluído, o que significa que este objeto estava entre dois outros objetos, ele criará um "buraco".

Isto é o que se chama fragmentação.
 3
Author: user455288, 2010-09-22 16:27:57

Quando você quer adicionar um item no heap o que acontece é que o computador tem que fazer uma busca de espaço para caber esse item. É por isso que alocações dinâmicas quando não feitas em um pool de memória ou com um alocador agrupado pode "atrasar" as coisas. Para uma aplicação STL pesada, se estiver a fazer multi-threading, existe o Hoard allocator ou a TBB Intel .

Agora, quando a memória está fragmentada, duas coisas podem ocorrer:
    Tem de haver mais. procura encontrar um bom espaço para colocar objetos "grandes". Isto é, com muitos objetos pequenos espalhados sobre encontrar um bom pedaço contíguo de memória poderia sob certas condições ser difícil (estes são extremos.) A memória não é uma entidade de leitura fácil. Os processadores estão limitados a quanto podem manter e onde. Eles fazem isso trocando páginas se um item que eles precisam é um lugar, mas os endereços atuais são outros. Se você está constantemente tendo que trocar páginas, o processamento pode abrandar (mais uma vez, cenários extremos onde este impacto desempenho.) Veja esta publicação em memória virtual .
 2
Author: wheaties, 2010-09-22 15:09:05

A fragmentação da memória ocorre porque são solicitados blocos de memória de diferentes tamanhos. Considere um buffer de 100 bytes. Pede dois caracteres, depois um inteiro. Agora você liberta os dois caracteres, em seguida, pedir um novo inteiro - mas esse inteiro não pode caber no espaço dos dois caracteres. Essa memória não pode ser reutilizada porque não está em um bloco contíguo grande o suficiente para re-alocar. Além disso, invocou muitas despesas de alocação para as suas notas.

Essencialmente, a memória só vem em blocos de um certo tamanho na maioria dos sistemas. Uma vez que dividam estes blocos, eles não podem ser reunidos até que todo o bloco seja libertado. Isto pode levar a blocos inteiros em uso quando na verdade apenas uma pequena parte do bloco está em uso.

A principal forma de reduzir a fragmentação dos heap é fazer atribuições maiores e menos frequentes. No extremo, você pode usar um heap gerenciado que é capaz de mover objetos, pelo menos, dentro de seu próprio código. Isso elimina completamente o problema - a partir de uma memória perspectiva, pelo menos. Obviamente, mover objetos e tal tem um custo. Na realidade, você só tem realmente um problema se você está alocando quantidades muito pequenas fora do Monte muitas vezes. O uso de contêineres contíguos (vetor, cadeia, etc) e alocação na pilha o mais humanamente possível (sempre uma boa idéia para o desempenho) é a melhor maneira de reduzi-lo. Isso também aumenta a coerência do cache, o que faz sua aplicação correr mais rápido.

O que você deve lembrar é que em um sistema de desktop x86 32bit, você tem um 2GB inteiro de memória, que é dividido em 4KB "páginas" (quase de certeza que o tamanho da página é o mesmo em todos os sistemas x86). Você terá que invocar alguma fragmentação omgwtfbbq para ter um problema. A fragmentação é realmente uma questão do passado, uma vez que os cabeçalhos modernos são excessivamente grandes para a grande maioria das aplicações, e há uma prevalência de sistemas que são capazes de suportá-lo, como cabeçalhos gerenciados.

 1
Author: Puppy, 2010-09-22 15:09:28