2011-11-17 10 views
8

Mi aplicación necesita mucha memoria y estructura de big data para realizar su trabajo. A menudo, la aplicación necesita más de 1 GB de memoria y, en algunos casos, mis clientes realmente necesitan usar la versión de 64 bits de la aplicación porque tienen varios gigabytes de memoria.Obligar a Windows a cargar DLL en lugares para que la memoria esté mínimamente fragmentada

En el pasado, podía explicar fácilmente al usuario que si la memoria alcanzaba de 1.6 a 1.7 GB de uso de memoria, estaba "sin memoria" o realmente cerca de una situación de "falta de memoria", y que necesitaban para reducir su memoria o pasar a una versión de 64 bits.

El año pasado noté que a menudo la aplicación solo usa aproximadamente 1 GB antes de que ya se quede sin memoria. Después de algunas investigaciones, parece que la causa de este problema es la fragmentación de la memoria. Utilicé VMMAP (una utilidad SysInternals) para ver el uso de la memoria de mi aplicación y vi algo como esto: Address Space Fragmentation

Las zonas de color naranja son asignadas por memoria por mi aplicación. Las áreas púrpuras son código ejecutable.

Como puede ver en la mitad inferior de la imagen, las áreas púrpuras (que son las DLL) se cargan en muchas direcciones diferentes, causando que mi memoria se fragmente. Esto no es realmente un problema si mi cliente no tiene una gran cantidad de datos, pero si mi cliente tiene conjuntos de datos que ocupan más de 1 GB, y una parte de la aplicación necesita un gran bloque de memoria (por ejemplo, 50 MB), puede dar como resultado una falla de asignación de memoria, causando que la aplicación se cuelgue.

La mayoría de mis estructuras de datos están basadas en STL ya menudo no requieren grandes porciones de memoria contigua, pero en algunos casos (por ejemplo, cadenas muy grandes), es realmente necesario tener un bloque contiguo de memoria. Desafortunadamente, no siempre es posible cambiar el código para que no necesite un bloque de memoria contiguo.

Las preguntas son:

  • Cómo puedo influir en la ubicación en la DLL se cargan en la memoria, sin necesidad de utilizar de manera explícita REBASE en toda la DLL en el equipo del cliente, o sin cargar todo el archivo DLL de forma explícita.
  • ¿Hay alguna manera de especificar las direcciones de carga de las DLL en su propio archivo de manifiesto de la aplicación?
  • ¿O hay una manera de decirle a Windows (a través del archivo de manifiesto?) Que no distribuya la DLL (creo que esta dispersión se llama ASLR).

Por supuesto, la mejor solución es a la que puedo influir desde el archivo de manifiesto de mi aplicación, ya que dependo de la carga automática/dinámica de las DLL por parte de Windows.

Mi aplicación es una aplicación de modo mixto (administrado + no administrado), aunque la mayor parte de la aplicación no está administrada.

¿Alguna sugerencia?

+0

¿Esto es algo que podría ayudarlo? http://msdn.microsoft.com/en-us/library/f7f5138s.aspx – detunized

+1

mmm, ¿realmente necesita tanta memoria al mismo tiempo? Process Monitor almacena sus registros en la memoria virtual y solo lleva los datos al espacio de direcciones de memoria del proceso cuando es necesario. Consulte http://blogs.msdn.com/b/oldnewthing/archive/2004/08/10/211890.aspx para obtener un código ejemplo –

+1

No estoy seguro de que todos los que dejan una respuesta comprendan las ramificaciones de ASLR: http://en.wikipedia.org/wiki/ASLR –

Respuesta

5

En primer lugar, la fragmentación de su espacio de direcciones virtuales no debe necesariamente causar la condición de falta de memoria. Este sería el caso si su aplicación tuviera que asignar contiguos bloques de memoria del tamaño apropiado. De lo contrario, el impacto de la fragmentación debería ser menor.

Usted dice que la mayoría de sus datos están "basados ​​en STL", pero si, por ejemplo, asigna un enorme std::vector, necesitará un bloque de memoria contiguo.

AFAIK no hay forma de especificar la dirección de asignación preferida de la DLL en su carga. De modo que solo hay dos opciones: volver a establecer la base (el archivo DLL) o implementar DLL cargándose usted mismo (lo cual no es trivial, por supuesto).

Por lo general, no es necesario volver a establecer la base de las DLL estándar de la API de Windows, ya que se cargan en su espacio de direcciones de forma muy precisa. La fragmentación puede llegar desde algunos archivos DLL de terceros (como ganchos de ventanas, inyecciones de antivirus, etc.)

3

No se puede hacer esto con un manifiesto, se debe hacer mediante la opción del vinculador/BASE. Linker + Advanced + Base address en el IDE. La forma más flexible es usar el/BASE: @ nombre de archivo, la sintaxis de la clave para que el vinculador lea la dirección base de un archivo de texto.

La mejor manera de llenar el archivo de texto es desde la ventana Debug + Windows + Modules. Obtenga la versión Release del programa cargado en el depurador y cargue todo el shebang. Depurar + Romper todo, abre la ventana y copia y pega en el archivo de texto. Edítelo para que coincida con el formato requerido, calculando las direcciones de carga desde la columna Dirección. Deje suficiente espacio entre los archivos DLL para que no tenga que modificar constantemente el archivo de texto.

+0

Por supuesto, esto no ayuda en absoluto con todas las DLL de hook encontradas en un sistema típico . Controlador gráfico Anti Virus. Controlador de mouse Etc. –

+0

Hmm, no, incluso esos pueden ser pirateados con la herramienta rebase.exe. No estoy tan seguro de que recomendaría hacer esto, perder el tiempo a menos que planee enviar su máquina con su producto. –

1

Si puede ejecutar parte de su propio código antes de que se carguen las bibliotecas en cuestión, puede reservar una buena porción de espacio de direcciones con anticipación para asignar desde.

De lo contrario, debe identificar las DLL responsables para determinar por qué se están cargando. Por ejemplo, ¿son parte de .NET, la biblioteca de tiempo de ejecución del lenguaje, su propio código o las bibliotecas de terceros que está utilizando?

Para su propio código, la solución más sensata es usar enlaces estáticos en lugar de dinámicos. Esto también debería ser posible para el tiempo de ejecución del idioma y puede ser posible para bibliotecas de terceros.

Para las bibliotecas de terceros, puede cambiar desde el uso de carga implícita a la carga explícita, de modo que la carga solo tenga lugar después de haber reservado su espacio de direcciones.

No sé si hay algo que pueda hacer con las bibliotecas .NET; pero como la mayoría de su código no está administrado, es posible eliminar los componentes administrados para deshacerse de .NET. O tal vez podría dividir las partes .NET en un proceso separado.

+0

... pero sería mucho más sensato refactorizar el programa según sea necesario para eliminar el requisito de un gran bloque de memoria contigua. –

Cuestiones relacionadas