¿Hacer un método estático ahorra memoria en una clase de la que tendrás muchas instancias?

8

En respuesta a la respuesta de Aaronaught a la pregunta en:

¿No puedo usar todos los métodos estáticos?

¿No se usa menos memoria para un método estático? Tengo la impresión de que cada instancia de objeto lleva consigo su propia versión ejecutable de una función miembro no estática.

Independientemente de la cantidad de sobrecarga involucrada en llamar a un método estático, independientemente del mal diseño de OO y los posibles dolores de cabeza en el futuro, ¿no usa menos memoria en tiempo de ejecución?

Aquí hay un ejemplo:

Hago un vector de objetos con inicialización cero. Cada objeto contiene una pieza de datos (un triángulo que consta de nueve dobles). Cada objeto se rellena en secuencia a partir de los datos leídos de un archivo .stl. Solo se necesita un método estático. El diseño adecuado de OO dicta que un método que se ocupa directamente de los datos debe distribuirse a cada objeto. Aquí está la solución estándar de OO:

foreach(obj in vec) {
  obj.readFromFile(fileName);
}

¡Cada obj lleva readFromFileel código compilado junto con los datos!

La memoria es más preocupante que el rendimiento en este caso, y hay MUCHOS datos en un sistema restringido.

Soluciones:

  1. Método de espacio de nombres (ideal para C ++ pero no posible en Java)
  2. Un método estático en la clase de obj. El código ejecutable se mantiene en un lugar en tiempo de ejecución. Hay una pequeña sobrecarga para llamar al método.
  3. Una clase padre de la que se deriva obj, que contiene el método privado readFromFile. Llamada con la super.callPrivateMethod()que llama readFromFile. Desordenado, y todavía algo de memoria en cada objeto.
  4. Implemente readFromFilefuera del alcance de obj, de modo que en la clase vec o en la clase llamante. Esto, en mi opinión, rompe la encapsulación de datos.

Me doy cuenta de que para grandes cantidades de datos, un objeto explícito para cada triángulo no es el mejor enfoque. Este es solo un ejemplo.

panlex
fuente
77
¿En qué idioma piensa que el código está duplicado para cada instancia? Esto no sucede en Java, C #, C ++.
joshp
2
Bueno, podría suceder con algún lenguaje dinámico en su infancia, o como un efecto secundario de la reflexión abusada. Pero en general, no, un byte no va a ser mucho más grande que el byte que contiene.
Matthew Mark Miller el
1
El único lenguaje que sé de dónde podría obtener este comportamiento de pérdida de memoria es Javascript, pero incluso entonces tendría que fallar por completo en prototipado OO para hacerlo. Lo que se supone que debes hacer en JS es poner métodos (tanto estáticos como no estáticos) en el objeto prototipo, de modo que todos los objetos que hereden de ese prototipo compartan esa función sin ninguna duplicación.
Ixrec
2
¿Ha considerado escribir un pequeño programa que crea miles de tales objetos y hacer las mediciones usted mismo?
Bryan Oakley
1
¿No es la respuesta a todas las preguntas relacionadas con OO: inyección de dependencia? Si le preocupa la sobrecarga de memoria, puede diseñar un objeto "protector" del que haga una instancia, que pueda inyectarse ...
Pieter B

Respuestas:

19

Los métodos no se almacenan por instancia, incluso con métodos virtuales. Se almacenan en una única ubicación de memoria, y solo "saben" a qué objeto pertenecen porque el thispuntero se pasa cuando los llama.

La única memoria adicional requerida en C ++ es si está utilizando métodos virtuales, que requieren un único puntero adicional por instancia para apuntar a la tabla de métodos virtuales (en Java, por supuesto, siempre tiene una clase base con métodos virtuales Object, por lo que es inevitable )

Si funcionara como lo describiste, ¡los idiomas OO no serían muy populares! Siéntase libre de agregar tantos métodos como necesite a sus objetos. No afectará su uso de memoria.

vrostu
fuente
También hay una sobrecarga por método virtual para cada clase. Y Java es virtual por defecto ...
Deduplicator
1
@Deduplicator: Eso es correcto, pero también puede argumentar que hay una sobrecarga por tener funciones / métodos en primer lugar. Cada función ocupa memoria y una función virtual requiere un poquito más (alrededor de 4 bytes).
Bart van Ingen Schenau
@BartvanIngenSchenau O 8 bytes, y para cada clase derivada nuevamente. Aunque ambas estimaciones están subestimando mucho el costo en Java ...
Deduplicador el
3
@Deduplicator Aún así, ese costo es por clase (y por clase derivada), no por instancia.
Craig
7

Independientemente de la cantidad de sobrecarga involucrada en llamar a un método estático, independientemente del mal diseño de OO y los posibles dolores de cabeza en el futuro, ¿no usa menos memoria en tiempo de ejecución?

Esto es bastante complicado, pero yo diría "sobre todo no".


fuente
2

Un método debe ser "estático" (muchos idiomas usan el término "método de clase", que es mucho mejor) si no se refiere a una instancia de la clase. Debería ser un método de instancia si se refiere a una instancia de la clase.

Tomar una decisión de diseño basada en la vaga posibilidad de algunos ahorros de memoria en lugar del uso adecuado de métodos de clase y métodos de instancia es una muy, muy, muy mala idea.

gnasher729
fuente
2
Un buen consejo, pero en realidad no responde la pregunta.
JacquesB
1
Con la fuerte suposición de que esta es una pregunta XY, lo hace.
gnasher729
1

En principio, solo hay una copia del código, ya sean funciones miembro estáticas o no:

  • En C ++ puede verificar esto fácilmente mirando la lista de ensambladores del código generado.
  • No soy un experto en Java, pero a partir de la especificación JNI puede deducir que es la misma lógica: puede buscar una sola referencia a una función y llamarla muchas veces para diferentes objetos (pasando una referencia de clase como argumento, así como el referencia de objeto si no es un método estático).

El uso de una función estática o no estática no cambiará la huella de la memoria.

En realidad, hay una diferencia muy pequeña, tanto en el código generado como en la memoria consumida en la pila durante el tiempo de ejecución de la función:

  • Una llamada a la función no estática requiere pasar una referencia / puntero al objeto en el que se aplica la función / método. Esto generalmente requiere un impulso adicional y una instrucción emergente en la secuencia de llamada.
  • Una llamada a una función estática no requiere esta sobrecarga.

Pero esto es realmente insignificante: estamos hablando de una o dos instrucciones de máquina más o menos en comparación con el código completo. Así que definitivamente no es algo de qué preocuparse.

La diferencia de consumo de la pila en tiempo de ejecución se limita al tiempo de ejecución de la función, al tamaño de un puntero. Esto también es insignificante, a menos que esté pensando en una función llamada recursivamente una gran cantidad de veces (varios millones de veces).

En conclusión, comparto totalmente la opinión de gnasher729 : la optimización prematura es la raíz de todo mal. Si la función es independiente del objeto, hágala estática y ese debería ser el único criterio.

Christophe
fuente
Creo que el compilador de C ++ podría evitar pasar la referencia a this, si no se usa en la función. En Java, probablemente JIT podría hacer esto.
Oliv
0

Es fácil concentrarse en cosas como optimizar el uso de la memoria o reducir las líneas de código, pero cuando comienza a tener que mantener programas que las personas usan / rompen y otros programadores agregan funcionalidad a ... y posiblemente introducen errores ... no es que su programa original posiblemente podría tener errores que pasaron las pruebas, verá que centrarse en la legibilidad / mantenimiento del código puede ser más importante. Podría hacer una matriz de bits de alta velocidad para rastrear una serie de puntos de datos, pero a menos que envuelva esa matriz de bits en un objeto con accesos comprensibles, el próximo programador que toque ese código probablemente no lo usará, lo reemplazará o le hará una brujería horrible. .

Jamy Spencer
fuente