Il concetto di base di un pool di memoria consiste nell'allocare una grande porzione di memoria per l'applicazione e, in seguito, invece di utilizzare semplicemente new
per richiedere memoria dall'O/S, si restituisce un blocco della memoria allocata in precedenza memoria invece.
Per fare in modo che questo funzioni, è necessario gestire l'utilizzo della memoria da soli e non può fare affidamento sull'O/S; Ad esempio, è necessario implementare le proprie versioni di new
e delete
e utilizzare le versioni originali solo durante l'allocazione, la liberazione o il ridimensionamento potenziale del proprio pool di memoria.
Il primo approccio sarebbe quello di definire la propria classe che incapsula un pool di memoria e fornisce metodi personalizzati che implementano la semantica di new
e delete
, ma prendono memoria dal pool pre-allocato. Ricorda, questo pool non è altro che un'area di memoria che è stata allocata utilizzando new
e ha una dimensione arbitraria. La versione del pool di new
/delete
risp. Resp. prendere i puntatori. La versione più semplice sarebbe probabilmente simile al codice C:
void *MyPool::malloc(const size_t &size)
void MyPool::free(void *ptr)
È possibile aggiungere questo colore ai modelli per aggiungere automaticamente la conversione, ad es.
template <typename T>
T *MyClass::malloc();
template <typename T>
void MyClass::free(T *ptr);
Si noti che, grazie agli argomenti di template, l'argomento size_t size
possono essere omessi dal momento che il compilatore consente di chiamare sizeof(T)
in malloc()
.
La restituzione di un puntatore semplice indica che il pool può crescere solo quando è disponibile memoria adiacente e si restringe solo se la memoria del pool ai suoi "bordi" non viene acquisita. Più specificamente, non è possibile riposizionare il pool perché ciò invaliderebbe tutti i puntatori restituiti dalla funzione malloc.
Un modo per correggere questa limitazione è di restituire i puntatori ai puntatori, ovvero restituire T**
anziché semplicemente T*
. Ciò consente di modificare il puntatore sottostante mentre la parte rivolta all'utente rimane la stessa. Incidentalmente, ciò è stato fatto per NeXT O/S, dove è stato chiamato "handle". Per accedere ai contenuti del manico, è stato necessario chiamare (*handle)->method()
o (**handle).method()
. Alla fine, Maf Vosburg ha inventato uno pseudo-operatore che ha sfruttato la precedenza degli operatori per sbarazzarsi della sintassi (*handle)->method()
: handle[0]->method();
Si chiamava sprong operator.
I vantaggi di questa operazione sono: In primo luogo, si evita il sovraccarico di una chiamata tipica di new
e delete
, e in secondo luogo, il vostro pool di memoria assicura che un segmento contiguo di memoria è utilizzata dall'applicazione, cioè, evita frammentazione della memoria e quindi aumenta gli hit della cache della CPU.
Quindi, in pratica, un pool di memoria fornisce una velocità di guadagno con lo svantaggio di un codice di applicazione potenzialmente più complesso. Ma poi di nuovo, ci sono alcune implementazioni di pool di memoria che sono dimostrati e possono essere semplicemente utilizzati, come ad esempio boost::pool.
Dai un'occhiata a [boost :: pool] (http://www.boost.org/doc/libs/1_58_0/libs/pool/doc/html/index.html) – rds504
Vedi anche: http: // stackoverflow.com/questions/16378306/c11-memory-pool-design-pattern – NathanOliver
Forse questo aiuta: [Gestione della memoria] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366779%28v = vs.85% 29.aspx) – Zero