2011-11-17 13 views
8

La mia applicazione richiede molta memoria e una grande struttura di dati per poter funzionare. Spesso l'applicazione richiede più di 1 GB di memoria e in alcuni casi i miei clienti hanno davvero bisogno di utilizzare la versione a 64 bit dell'applicazione perché hanno diversi gigabyte di memoria.Forzare Windows per caricare le DLL in modo che la memoria sia minimamente frammentata

In passato, potevo facilmente spiegare all'utente che se la memoria raggiungeva da 1,6 a 1,7 GB di utilizzo della memoria, era "a memoria insufficiente" o realmente vicino a una situazione di "esaurimento della memoria", e di cui avevano bisogno per ridurre la memoria o passare a una versione a 64 bit.

L'anno scorso ho notato che spesso l'applicazione utilizza solo circa 1 GB prima che finisca la memoria. Dopo alcune indagini sembrava che la causa di questo problema fosse la frammentazione della memoria. Ho usato VMMAP (un'utilità SysInternals) per esaminare l'utilizzo della memoria della mia applicazione e ho visto qualcosa del genere: Address Space Fragmentation

Le aree arancioni sono memoria allocata dalla mia applicazione. Le aree viola sono codice eseguibile.

Come si può vedere nella metà inferiore dell'immagine, le aree viola (che sono le DLL) sono caricate in molti diversi indirizzi, causando la frammentazione della memoria. Questo non è un problema se il mio cliente non ha molti dati, ma se il mio cliente ha set di dati che richiedono più di 1 GB e una parte dell'applicazione richiede un grande blocco di memoria (ad esempio 50 MB), può causare un errore di allocazione della memoria, causando l'arresto anomalo dell'applicazione.

La maggior parte delle mie strutture dati sono basate su STL e spesso non richiedono grossi blocchi di memoria contigua, ma in alcuni casi (ad esempio stringhe molto grandi), è davvero necessario avere un blocco di memoria contiguo. Sfortunatamente, non è sempre possibile modificare il codice in modo che non abbia bisogno di un blocco di memoria contiguo.

Le domande sono:

  • Come posso influenzare la posizione in cui DLL vengono caricati in memoria, senza esplicito utilizzando REBASE su tutto il DLL sul computer del cliente, o senza caricare tutti i DLL in modo esplicito.
  • C'è un modo per specificare gli indirizzi di carico delle DLL nel file manifest dell'applicazione?
  • Oppure c'è un modo per dire a Windows (tramite il file manifest?) Di non disperdere le DLL (penso che questa diffusione sia chiamata ASLR).

Naturalmente la soluzione migliore è quella che posso influenzare dal file manifest della mia applicazione, poiché mi affido al caricamento automatico/dinamico delle DLL da parte di Windows.

La mia applicazione è un'applicazione in modalità mista (gestita + non gestita), sebbene la maggior parte dell'applicazione non sia gestita.

Qualche suggerimento?

+0

È qualcosa che potrebbe aiutarti? http://msdn.microsoft.com/en-us/library/f7f5138s.aspx – detunized

+1

mmm, hai davvero bisogno di molta memoria allo stesso tempo? Process Monitor memorizza i suoi registri nella memoria virtuale e porta i dati nello spazio degli indirizzi di memoria del processo solo quando necessario, controlla http://blogs.msdn.com/b/oldnewthing/archive/2004/08/10/211890.aspx per un codice esempio –

+1

Non sono sicuro che tutti lascino una risposta per capire le ramificazioni di ASLR: http://en.wikipedia.org/wiki/ASLR –

risposta

5

In primo luogo, la frammentazione dello spazio degli indirizzi virtuale non deve necessariamente causare la condizione di esaurimento della memoria. Questo sarebbe il caso se l'applicazione dovesse allocare blocchi di memoria contigui della dimensione appropriata. Altrimenti l'impatto della frammentazione dovrebbe essere minore.

Si dice che la maggior parte dei dati è "basata su STL", ma se ad esempio si assegna un enorme std::vector è necessario un blocco di memoria contiguo.

AFAIK non è possibile specificare l'indirizzo di mapping preferito della DLL al momento del caricamento. In modo che ci siano solo due opzioni: rebase (il file DLL) o implementa la DLL caricando te stesso (che non è banale, ovviamente).

In genere non è necessario rebase delle DLL API standard di Windows, vengono caricate molto nello spazio indirizzo. La frammentazione può arrivare da alcune DLL di terze parti (come ganci di Windows, iniezioni di antivirus e così via)

3

Non è possibile eseguire questa operazione con un manifest, deve essere eseguita dall'opzione linker/BASE. Linker + Advanced + Indirizzo base nell'IDE. Il modo più flessibile consiste nell'utilizzare/BASE: @ nomefile, sintassi della chiave in modo che il linker legga l'indirizzo di base da un file di testo.

Il modo migliore per ottenere il file di testo pieno è dalla finestra Debug + Moduli di Windows +. Scarica il build Release del tuo programma caricato nel debugger e carica l'intero shebang. Debug + Break All, porta la finestra e copia-incolla nel file di testo. Modificalo in modo che corrisponda al formato richiesto, calcolando gli indirizzi di caricamento dalla colonna Indirizzo. Lascia abbastanza spazio tra le DLL in modo da non dover modificare continuamente il file di testo.

+0

Naturalmente questo non aiuta affatto con tutte le DLL di aggancio trovate su un sistema tipico . Driver grafico. Anti Virus. Driver del mouse. Etc. –

+0

Hmm, no, anche quelli possono essere violati con lo strumento rebase.exe. Non sono sicuro di raccomandare di farlo, perdita di tempo a meno che non si preveda di spedire la macchina con il prodotto. –

1

Se è possibile eseguire parte del proprio codice prima che le librerie in questione siano caricate, è possibile riservarsi un bel po 'di spazio di indirizzi prima del tempo da cui effettuare l'allocazione.

In caso contrario, è necessario identificare le DLL responsabili, per determinare il motivo per cui vengono caricate. Ad esempio, fanno parte di .NET, della libreria di runtime della lingua, del tuo codice o delle librerie di terze parti che stai utilizzando?

Per il proprio codice la soluzione più sensata è probabilmente quella di utilizzare il collegamento statico anziché dinamico. Ciò dovrebbe anche essere possibile per il runtime della lingua e potrebbe essere possibile per le librerie di terze parti.

Per le librerie di terze parti, è possibile passare dall'utilizzo del caricamento implicito al caricamento esplicito, in modo che il caricamento avvenga solo dopo aver prenotato il proprio spazio di indirizzi.

Non so se c'è qualcosa che puoi fare sulle librerie .NET; ma poiché la maggior parte del tuo codice non è gestita, potrebbe essere possibile eliminare i componenti gestiti per sbarazzarsi di .NET. O forse potresti dividere le parti .NET in un processo separato.

+0

... ma sarebbe molto più sensato rifattare il programma come necessario per eliminare il requisito di un grande blocco di memoria contiguo. –

Problemi correlati