I problemi di utilizzo e il riutilizzo di molti oggetti di varie dimensioni in una domanda può causare frammentazione del mucchio che può rallentare la velocità di elaborazione. Questo articolo utilizza una fabbrica oggetto, per effettuare e riciclare gli oggetti con il minimo di effetti di frammentazione.
Uno dei modelli classici nel libro "Design Patterns; Elementi di Riutilizzabile Object-Oriented Software" è il metodo di fabbrica, dove gli oggetti vengono creati di recente e ho dovuto usare. Il motivo? Un problema di frammentazione della memoria in un sistema di simulazione che utilizzano un gran numero di oggetti da tenere numerico array. Ci sono stati diversi tipi di array numerico, in dimensioni che variano da 1500 x 1 a 5000 x 10 o numeri interi o doppie. Questi sono stati utilizzati in una simulazione di un calcolo finanziario nel corso di un periodo di due anni, la creazione di dati su ciascuno dei 472 giorni dalla simulata dati storici. Su ogni giorno, è stato leggere i dati da un database, trasformati, poi salvati fuori.Nonostante le ampie perdite di controllo, il software solo per eseguire una serie di simulazioni di giorni prima di mangiare il file di swap di Windows. Perdite di memoria sono stati attentamente monitorati ed eliminati, ma i miei capelli erano ancora in pericolo. L'app non funzionare per più di circa 30 giorni prima della simulazione era impegnato oltre la metà dei file di paging-NT su un sistema di 512 MB di RAM! Chiudi inchiesta ha rivelato che ogni giorno si ha la memoria allocata da 5M è cresciuto di circa 80MB poi ridursi di circa 5M di nuovo. Alla luce di tutto ciò, non un problema con 512Mb di giocare, ma guardando il Task Manager di Windows NT ha mostrato una crescente quantità di memoria impegnata.
FRAMMENTAZIONE
Il problema è semplice, è stato per colpa di heap frammentazione. Ciò si verifica quando diversi oggetti vengono creati e poi distrutto più volte. Come ogni oggetto viene creato, si consuma la memoria heap. Se gli oggetti sono stati creati e poi distrutti, in ordine inverso probabilmente non accadrà come tutte le liberato memoria potrebbero essere fusi in un unico grande blocco. Ma in ogni sistema con un gran numero di oggetti d'ordine della creazione e della distruzione non sarà mai il mio simmetrico-app può facilmente avere fino a 50.000 oggetti in memoria allo stesso tempo. Così, quando un oggetto è distrutto, un puntatore al blocco di memoria libera è aggiunto a un blocco libero.Quando le altre richieste di memoria sono, Windows tenta di allocare tali richieste dei liberati prima lista. La frammentazione si verifica quando un grande blocco di 1Mb, come è stato richiesto e successivamente liberato, seguita da una richiesta di un piccolo blocco. Questo è tratto dal primo blocco sulla libera-lista che può essere il 1Mb, che lascia libero solo 900KB. Poi un'altra domanda di un grande blocco di 1Mb arriva, non può essere soddisfatto dalla lista libera e così è presa dal mucchio. Se la creazione / distruzione ciclo volte succede abbastanza grandi blocchi sulla heap sono tagliate in piccoli pezzi, la RAM fisica è esaurito e sostituito con ram virtuale. Gestione della memoria heap di Windows (e Delphi) è abbastanza intelligente, ci vuole un sacco per farlo frammento. Ma sotto la pressione continua di un gran numero di oggetti creati e distrutti, la gestione della memoria gradualmente grotta pollici
Quando Windows si esaurisca di RAM libera, inizia a scambiare pagine di ram su disco e le prestazioni assume un naso immersione. Vostra applicazione potrebbe essere galoppante felicemente insieme al 100% della CPU fino a quando inizia a scambiare. E allora diventa una marcia funebre di scansione a lungo forse 7-10% della CPU, tenendo sempre a correre. Disaster!
Microsoft hanno fatto un grande sforzo che il gestore della memoria per quanto possibile efficiente. Ad esempio sotto NT, c'è un processo in due fasi e di riservare commettere memoria. Se il tuo richiede circa 100Mb, è riservata, quando l'applicazione viene caricata. Ma solo quando la memoria si accede alla riserva pagine sono impegnati. Se volete saperne di più di quello che sarà sempre bisogno di sapere su questo e altri argomenti, raccomando il libro Inside NT, pubblicato da Microsoft Press, ma ottenere il David Salomone, che è la versione successiva edizione, non Helen Custer prima edizione.
FABBRICA DEL MODELLO
Così ho bisogno di un modo di non frammentare la creazione di molti oggetti, che li utilizzano, gettando via e poi farlo di nuovo tutte le finestre senza esaurirsi di memoria virtuale. Dopo aver letto il libro ho pensato pattern perché non usare una fabbrica, vale a dire una fabbrica oggetto che crea gli oggetti di una determinata classe. Poi uno è andato meglio e reso ecocompatibili, così si arriva a riciclare tutti i suoi oggetti fabbricati, invece di distruggere loro e senza i relativi problemi di frammentazione. La ciliegina sulla torta è stato quello di rendere la fabbrica in grado di espandere le proprie capacità senza perdita di velocità di accesso.
Piuttosto che avere una fabbrica di classe per tutti i tipi di classe, ho preso l'approccio più semplice che passa l'oggetto di classe in fabbrica, come parametro di creazione di una fabbrica. Quando la fabbrica si è creato specificare sia la classe di oggetti è possibile effettuare e la capacità di stoccaggio iniziale della fabbrica. Questo formato può essere modificato al rialzo chiamando il metodo GrowFactory. Vi suggerisco di chiamare questo solo in casi eccezionali (!) Circostanze.
Il link per la fabbrica di ogni oggetto è necessario, in modo che tutti i "fabbrica di oggetti" (QOR) deve discendere da una classe TFactoryObject invece di Tobject. Questo aggiunge una fabbrica di riferimento che è "timbro" su tutti i QOR in modo che l'oggetto che sa di fabbrica da utilizzare per riciclare se stessa.
Invece di creare un oggetto vostra applicazione richiede uno dalla fabbrica adatto chiamando la sua RequestObj metodo che restituisce un oggetto tclass indietro e si converte a destra classe usando 'come'. Infine, quando hai finito di usare l'oggetto che hai appena chiamata RecycleSelf suo metodo. N. creazione o la distruzione, ad eccezione degli stabilimenti stessi.
COME FUNZIONA
Quando la fabbrica è stato creato, tutti gli oggetti creati sono fisicamente in un blocco di ram. Un tlist (fblocklist) oggetto detiene l'indirizzo di ciascuno di questi blocchi. Ogni volta che si fa crescere la fabbrica, un nuovo blocco è stato creato e aggiunto a questo elenco. Il metodo AddObjects crea il numero specificato di oggetti utilizzando l'ariete dal blocco. Se si scrive codice come questo essere consapevoli del fatto che solo facendo un tobject (indirizzo) non è sufficiente per creare l'oggetto. È sempre necessario chiamare ObjectClass.InitInstance (indirizzo) per trasformarlo in un 'buon' oggetto. InitInstance cancella tutto a zero, zero, ecc, ma ancora più importante che istituisce il VMT.
La fabbrica contiene anche un altro tlist (ffreelist) che contiene l'indirizzo di ogni oggetto non utilizzati.
Tutti i lavori di riempimento asino fabbrica è fatto in privato metodo AddObjects. Per ogni oggetto creato nel blocco, questo il puntatore ptr converte in un oggetto, usando FactoryObject come la classe di oggetto creato. Questo deve essere sempre un discendente di TfactoryObject. Ob detiene l'oggetto di riferimento e link fabbrica al fabbricato oggetto. ptr è poi incrementata a punto per il prossimo oggetto in blocco con l'aggiunta di fsize.
Per ottenere un oggetto che si chiama Request_Obj r codice di riferimento che salta fuori la fine del ffreelist e restituisce il primo oggetto richiesto. Il riciclaggio è il contrario, che spinge l'oggetto riciclato riferimento alla fine di ffreelist. Una cosa da tenere a mente. Quando una fabbrica fatta oggetto è in uso, la fabbrica non ha alcun riferimento ad esso, anche se la memoria occupata da l'oggetto è contenuto all'interno della fabbrica!
SOSTITUZIONE CREARE E DISTRUGGERE
Utilizzando la fabbrica richiede l'uso di oggetti fabbricati leggermente diverso dal normale. Non è più libero di creare o esplicitamente, si è invece richiesta la fabbrica pertinenti per l'oggetto. A meno che la fabbrica è sempre vuoto questo lavoro. È necessario modificare il codice di inizializzazione del costruttore e la cessazione del codice destructor routine. Ci sono due approcci.
1) Se l'oggetto è un semplice creare costruttore senza parametri, è possibile rinominare a procedura di init; ignorare e rimuovere qualsiasi ereditato creare le chiamate. La fabbrica chiama sempre uno Init metodo qualsiasi oggetto quando viene richiesto. Per default non fa nulla, ma è possibile ignorare questo modo il tuo Init sarà chiamato automaticamente su ogni oggetto richiesto dalla fabbrica.
2) Se la tua iniziale è creare parametri, rinominarlo in qualcosa di simile Inizializza e rimuovere ereditato etc chiamate. Dopo l'oggetto è richiesto inizializzare la chiamata di routine, ad esempio MyRoutine.Initialise (...)
Se l'oggetto ha destructor codice, rinominare a procedura Fatto; ignorare in modo tale che si chiama automaticamente quando l'oggetto viene riciclato. Fatto Init e sono simili a creare / distruggere, ma senza il bagaglio del meccanismo di costruzione o la distruzione.
Come una leggera digressione, mi sembra di capire che ci sono argomenti in Delphi mondo in favore da una parte la creazione o due Una parte è parte in cui il costruttore ha completamente parametri e definisce l'oggetto. Nei due approccio parte, solo il costruttore crea un oggetto vuoto che è poi inizializzato da un secondo metodo. La fabbrica approccio Credo fermamente nelle due parti del campo ..
Quando la fabbrica è distrutta, tutti i blocchi di memoria sono assegnati liberato. Prima di questo, la fabbrica dei controlli che il numero di oggetti in freelist partite la capacità. Se hai dimenticato di riciclare tutti i restanti oggetti, intende sollevare un'eccezione.
PROGRAMMA DI CONFRONTO
Ciò dimostra i vantaggi di fabbriche .. Al lavoro la mia applicazione ha molte dimensioni diverse di oggetti in quantità diverse, ma una linea 15.000 programma non esattamente essere pubblicabile. Dopo un po 'di trial and error "Sono venuto con un breve programma che può mostrare la frammentazione. Tuttavia, questo dipende dalle dimensioni dei diversi oggetti, quanti sono, RAM disponibile e per quanto tempo viene eseguito. Inoltre, la frammentazione sembra verificarsi più rapidamente rispetto NT4.0 su 98 che suggerisce che forse 98 è dotato di una migliore gestione della memoria. Quando viene eseguito crea e distrugge un gran numero di oggetti più volte per il numero specificato di giorni. E 'anche non esattamente la stessa cosa usando una fabbrica. Ogni giorno è scaduta e le due serie di volte tracciate utilizzando Tchart.Ho usato tre tipi di oggetti, tutti disceso dal tTestobj che scende da Tfactoryobject. 2K sono oggetti di piccole dimensioni in termini di dimensioni, medie e grandi sono 40K sono 800K, ma le dimensioni sono definite da costanti e facilmente cambiato. La demo del programma assegna 100Mb per la normale di oggetti e un altro 100Mb per la fabbrica. Entrambi contengono lo stesso numero di oggetti-diviso in parti uguali per dimensioni tra i tre tipi di oggetto non ci sono 17406 oggetti di piccole dimensioni, 873 medie e 43 grandi. Nella procedura di creazione oggetto TimeOneDay sono casualmente creato e aggiunto alla lista. Alla fine della giornata sono liberato e il processo ripetuto giorno successivo.Lo stesso viene fatto con tre stabilimenti con tutti gli oggetti fabbricati aggiunto a un elenco factorydata. Su un P2 400 con 256Mb esecuzione di 100 giorni, non vi è stato un modesto aumento nel tempo sia per la fabbrica e il normale oggetto creazione. Commentando la creazione normale ha confermato tale sospetto, l'aumento è stato molto meno per un giorno 1000 utilizzando fabbriche 100 giorni contro i normali e con entrambe le prove in fabbrica. Ho pensato che questo è stato causato da un aumento della pagina scambio a causa della frammentazione che interessano entrambi i processi. Alcune altre combinazioni di dimensioni e il numero di oggetti in Windows 98 non ha mostrato alcuna frammentazioneHo applicato questa originariamente utilizzando un tlist per entrambi testdata e factorydata e cambiata in un secondo momento, quando ho capito che più volte aggiungendo e liberando 18000 puntatori è stato anche l'aggiunta di heap frammentazione. Io non sono completamente a favore ammaraggio tstringlists nel codice-sono molto utili (come sono tlists), ma se si deve manipolare un gran numero di voci, potrebbe essere meglio utilizzare il proprio elenco di strutture. Se si desidera utilizzare tlist o Tstringlist per questo scopo, probabilmente la sua migliore per riempire completamente la struttura con zero puntatori, in modo che count = capacità e utilizzare un numero intero di tenere l'indice degli ultimi puntatore.
Questo app ha confermato inoltre che il codice di fabbrica è molto veloce a ripartizione e la cancellazione-tipicamente 30 ms per 18000 oggetti invece di 870 ms che normale creazione / soppressione ha.
------------------------------------------------
Tgis articolo apparso originariamente nella rivista sviluppatori Delphi. Sources.zip

Delicious
Digg
Google
Yahoo