É possível marcar um segmento de memória como "fora dos limites" para que o gerente de heap não aloque dele?
-
28-09-2019 - |
Pergunta
Hoje cedo eu perguntei essa questão.
Depois de passar algum tempo investigando esse problema, descobri o que está acontecendo. Estou postando isso como uma nova pergunta, porque acho interessante o suficiente para rastrear como um problema separado. Vou atualizar essa pergunta com a resposta (e um link para este).
Teste de unidade de lançamento do depurador
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194 /* Different memory location */
Lançamento do teste de unidade da linha de comando
// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960
// Lots of other code
// ...
// Destroy object
delete pObject;
// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960 /* Same memory location */
Resumindo:
- Ao iniciar o teste de unidade a partir do linha de comando, chamadas subsequentes para
new
para alocar umObject
(delete
ing the anteriorObject
antes de alocar um novo) sempre retorne o mesmo endereço na memória. - Ao iniciar o teste de unidade a partir do Depurador, chamadas subsequentes para
new
para alocar umObject
(delete
ing the anteriorObject
antes de alocar um novo) sempre retorne um único endereço na memória.
O problema é isso porque as alocações de Object
Sempre obtenha o mesmo endereço na memória ao iniciar a linha de comando, um mapa que estou acessando que armazenou o velho O ponteiro ainda pode ser usado e o teste não falha. Mas quero que meu teste de unidade falhe quando a correção do defeito não estiver no lugar, para garantir que ela não falhe silenciosamente e o defeito não volte.
Existem 2 partes na minha pergunta:
Por que o gerente Heap reutilizaria a mesma parte da memória ao iniciar um teste de unidade a partir da linha de comando, mas não quando inicio o teste da unidade do depurador?
Existe uma configuração do compilador que eu poderia usar no meu chicote de teste ou um método que posso ligar para impedir que o gerente de heap reusuga uma seção de memória que excluí, para me permitir escrever corretamente meu teste de unidade? 1
1Obviamente, uma maneira de fazer isso é não excluir o objeto original, mas a parte do código que aloca isso está no meu código de produção, e isso resultaria em vazamentos de memória.
Solução
Seu teste de unidade é falha, pois está dependendo do comportamento indefinido. Você deve reescrever seu teste de unidade para que ele não dependa de comportamento indefinido; nesse caso, ele sempre passará, independentemente de como o gerenciador de memória decide alocar memória.
O que você está fazendo é o seguinte:
Object* pObject = new Object(...);
...
delete pObject;
pObject = new Object(...);
// Use dangling pointer to first object, and if it crashes, the unit test fails
// This is WRONG since a crash isn't guaranteed
Em vez disso, você deve reestruturar o teste de unidade para que funcione assim:
Object* pObject = new Object(...);
...
// Check to see if there are dangling references to pObject right before we
// delete it. If there are, assert() and fail the unit test.
assert(NoDanglingReferences(pObject));
delete pObject;
// Continue on with more tests
Outras dicas
Você pode substituir new
e delete
com suas próprias versões que têm o comportamento que você deseja.
Primeiro de tudo - não em um gerenciador de memória "normal". Depois de negociar a memória, você passa para o gerenciador de memória e o último pode reutilizá -lo.
Você poderia Escreva um gerente personalizado Como Usuário Andreas Brinck sugere, mas o que faria? Ele não cria memória do ar, solicita -a de algum lugar como heap CRT ou pilha do sistema operacional.
Cenário A. Não retornaria a memória à pilha subjacente - você terá um vazamento e o bloco de memória ainda será mapeado no espaço de endereço e estará acessível.
Cenário B. Ele retornará a memória à pilha subjacente - então, quando seu manober tentar alocar a memória novamente, o heap subjacente pode retornar esse bloco novamente. Além disso, você não sabe o que o heap subjacente faz quando retorna a memória. Pode torná -lo não mapeado ou não - portanto, acessar que a memória pode travar ou não.
A linha inferior é que você está ferrado. Tentar testar comportamento indefinido não será muito produtivo.
Este é um exemplo de comportamento indefinido. Nem C ++ nem o gerenciador de heap definem como a memória será alocada. Você não pode confiar na memória de ser reutilizada ou não ser reutilizada. Quando você faz algo como acima, não há como determinar ou mudar se o ponteiro retornado será ou não diferente do primeiro alocado.