Tenga cuidado con lo que tipo de memoria está hablando.
El código compilado para C utiliza comparativamente poca memoria para el código de máquina compilada en sí.
Esperaría que el bytecode de Python para un algoritmo determinado fuera realmente más pequeño que el código C compilado para un algoritmo similar, porque las operaciones de bytecode de Python son de un nivel mucho mayor, por lo que a menudo hay menos de ellas para hacer algo. Pero un programa de Python también tendrá el código compilado del intérprete de Python en la memoria, que es un programa bastante grande y complejo en sí mismo. Además, un programa típico de Python tendrá mucha más biblioteca estándar en la memoria que un programa típico de C (y un programa de C puede eliminar todas las funciones que en realidad no usa si está vinculado estáticamente, y si está vinculado dinámicamente, entonces comparte el código compilado con cualquier otro proceso en memoria que lo use).
PyPy tiene encima de esto el código de máquina del compilador JIT, así como el código de máquina generado a partir del bytecode de Python (que no desaparece, debe mantenerse también). Por lo tanto, su intuición (que un sistema JITed "debería" consumir memoria en algún lugar entre la de un lenguaje compilado y un lenguaje totalmente interpretado) no es correcta de todos modos.
Pero además de todo eso, usted tiene la memoria real utilizada por las estructuras de datos en las que opera el programa. Esto varía enormemente, y tiene poco que ver con si el programa se compila antes de tiempo, o se interpreta, o se interpreta y se JIT.Algunas optimizaciones del compilador reducirán el uso de la memoria (ya sea que se apliquen con anticipación o justo a tiempo), pero en realidad muchos intercambian el uso de la memoria para ganar velocidad. Para los programas que manipulan cualquier cantidad seria de datos, empequeñecerá por completo la memoria utilizada por el código en sí, de todos modos.
Cuando dicen:
En lugar de un programa JIT'ed (como PyPy) consumen varias veces más memoria que el programa interpretado equivalente (como Python). ¿Por qué?
¿En qué programas estás pensando? Si has hecho alguna comparación, supongo que por tu pregunta estarían entre PyPy y CPython. Sé que muchas de las estructuras de datos de PyPy son en realidad más pequeñas que las de CPython, pero una vez más, eso no tiene nada que ver con el JIT.
Si el uso dominante de memoria de un programa es el código en sí, un compilador JIT agrega gran sobrecarga de memoria (para el compilador y el código compilado) y no puede hacer mucho para "recuperar "uso de memoria a través de la optimización. Si el uso dominante de la memoria son las estructuras de datos del programa, entonces no me sorprendería en absoluto que PyPy use mucha menos memoria que CPython, esté o no habilitado el JIT.
No hay realmente una respuesta directa a su "¿Por qué?" porque las declaraciones en su pregunta no son directamente ciertas. Qué sistema usa más memoria depende de muchos factores; la presencia o ausencia de un compilador JIT es un factor, pero no siempre es significativo.
Todo el mundo habla de que el JIT es la causa del aumento en el uso de memoria en Pypy, pero esa no es la historia completa. Si bien algunas de las estructuras de memoria de Pypy son más compactas (listas de todas las entradas, por ejemplo), Pypy tiene una variedad de recolectores de basura que se pueden usar con ella y definitivamente afectarán la cantidad de memoria en uso. El recolector de basura predeterminado actual en Pypy no hace recuento de referencias por razones de velocidad. Debido a esto, los objetos pueden permanecer en la memoria más tiempo de lo que lo harían en CPython y, por lo tanto, los programas pueden tener una mayor huella de memoria en Pypy. –