2010-01-20 6 views
7

He escrito una biblioteca en C que consume mucha memoria (millones de pequeños bloques). He escrito un programa c que usa esta biblioteca. Y he escrito un programa Java que usa la misma biblioteca. El programa Java es una capa muy delgada alrededor de la biblioteca. Básicamente, solo hay un método nativo que se llama, hace todo el trabajo y regresa horas después. No hay comunicación adicional entre Java y la biblioteca nativa usando la interfaz de invocación java. Tampoco hay un objeto Java que consuma una cantidad notable de memoria.¿Por qué una biblioteca nativa usa 1.5 veces más memoria cuando es utilizada por Java que cuando es usada por un C-Programm bajo Linux?

Así que el programa c y el programa Java son muy similares. Toda la asignación de computación/memoria ocurre dentro de la biblioteca nativa. Todavía. Cuando se ejecuta, el programa c consume 3GB de memoria. ¡Pero el programa Java consume 4.3GB! (Cantidad de VIRT informada por la parte superior)

Comprobé el mapa de memoria del proceso de Java (usando pmap). Las bibliotecas solo usan 40MB. Por lo tanto, las bibliotecas adicionales cargadas por Java no son la causa.

¿Alguien tiene una explicación para este comportamiento?

EDIT: Gracias por las respuestas hasta el momento. Para hacerlo un poco más claro: ¡El código java no hace más que invocar la biblioteca nativa UNA VEZ! El montón de Java es de tamaño estándar (quizás 60 MB) y no se usa (excepto para la clase que contiene el método principal y la otra clase que invoca la biblioteca nativa).

El método de la biblioteca nativa es de larga ejecución y realiza muchos mallocs y libera. La fragmentación es una explicación en la que también pensé. Pero dado que no hay código Java activo, el comportamiento de fragmentación debería ser el mismo para el programa Java y el programa c. Como es diferente, también supongo que las implementaciones malloc utilizadas son diferentes cuando se ejecutan en el programa c o en el programa Java.

+0

Observación interesante. No hubiera esperado este comportamiento. – x4u

+0

fragmentación de montón malloc. Ver http://stackoverflow.com/a/28935176/166062 –

+0

@LariHotari Aunque el motivo de esta pregunta era diferente (ver mi respuesta), también tuvimos problemas con la fragmentación. Lo resolvimos cambiando a implementaciones alternativas como tcmalloc, jemalloc o la implementación malloc de locklessinc. –

Respuesta

2

Lo siento chicos. Suposiciones erróneas

Me acostumbré a los 64MB que las implementaciones de Sun Java usaban para el tamaño de montón máximo predeterminado. Pero utilicé openjdk 1.6 para probar. Openjdk usa una fracción de la memoria física si no se especificó explícitamente un tamaño máximo de almacenamiento dinámico. En mi caso, un cuarto. Usé una máquina de 4GB. Un cuarto es por lo tanto 1GB. Ahí está la diferencia entre C y Java.

Lamentablemente, este comportamiento no está documentado en ninguna parte. Lo encontré mirando el código fuente de openjdk (arguments.cpp):

// If the maximum heap size has not been set with -Xmx, 
// then set it as fraction of the size of physical memory, 
// respecting the maximum and minimum sizes of the heap. 
0

Hay diferentes factores que debe tener en cuenta especialmente en un lenguaje como Java, Java se ejecuta en una máquina virtual y la recolección de basura es manejada por Java Runtime, ya que hay un esfuerzo considerable (me imagino) de usar la interfaz de invocación de Java para cambiar o ejecutar el método nativo dentro de la biblioteca nativa, ya que tendría que haber un medio para asignar espacio en la pila, cambiar a código nativo, ejecutar el método nativo, volver a la máquina virtual Java y quizás de alguna manera , el espacio en la pila no fue liberado, eso es lo que me inclinaría a pensar.

Espero que esto ayude, Saludos cordiales, Tom.

3

Simplemente adivinando: puede estar utilizando una implementación no predeterminada malloc cuando se ejecuta dentro de la JVM que se ajusta a las necesidades específicas de la JVM y produce más sobrecarga que la malloc de uso general en su implementación libc normal.

+0

. Esa también sería mi suposición. Pero no te doy el +1 hasta que haya evidencia de que realmente es el caso. Sin embargo, las bibliotecas – Omnifarious

1

Java necesita tener memoria continua para su almacenamiento dinámico, por lo que puede asignar el tamaño máximo de memoria como memoria virtual. Sin embargo, esto no consume memoria física y puede que ni siquiera consuma swap. Verificaría cuánto aumenta su memoria residente.

+0

llamadas por JVM aún pueden liberar la memoria asignada. –

+0

Una posibilidad es que haya un subproceso de Java ejecutándose simultáneamente con el código nativo y están fragmentando la memoria. El OP dice que hay muchas asignaciones pequeñas. – Omnifarious

+0

El tamaño máximo de almacenamiento dinámico para Java se establece en 60 MB aproximadamente. Esta no puede ser la razón para consumir 1GB más de memoria. Aún así: la memoria residente es de 3GB, como el programa c. –

0

Es difícil de decir, pero creo que el corazón del problema es que hay dos montones en su aplicación que deben mantenerse: el heap Java estándar para las asignaciones de objetos Java (mantenido por la JVM), y el montón C que se mantiene mediante llamadas a malloc/free. Es difícil decir qué está sucediendo exactamente sin ver un código.

0

Aquí hay una sugerencia para combatirlo.

Haga que el código C se detenga con la llamada malloc estándar, y use una versión alternativa de malloc que capture la memoria mmap ing /dev/zero. Puede modificar una implementación de malloc desde una biblioteca o hacerla funcionar si se siente lo suficientemente competente como para hacerlo.

Sospecho que descubrirá que su problema desaparece después de hacer eso.

Cuestiones relacionadas