¿Deberíamos evitar la creación de objetos en Java?


243

Un colega me dijo que en Java la creación de objetos es la operación más costosa que podría realizar. Así que solo puedo concluir para crear la menor cantidad de objetos posible.

Esto parece algo para derrotar el propósito de la programación orientada a objetos. Si no estamos creando objetos, ¿solo estamos escribiendo un estilo largo de clase C, para la optimización?


39
"La creación de objetos Java es la operación más costosa que podría realizar" . ¿Te importaría compartir la fuente de esa afirmación?
Songo

92
Y, ¿la operación más cara en comparación con qué? En comparación con el cálculo de pi a 900 dígitos o en comparación con el incremento de un int?
Jason

44
¿Podrían haber estado hablando de una parte particular de la creación de ese objeto específico? Estoy pensando en cómo Apple usa una cola para tableViewCells. ¿Quizás su colega sugirió crear algunos objetos y reutilizarlos debido a una sobrecarga asociada con esos objetos específicos? Solo trato de averiguar por qué harían tal afirmación.
Tyler DeWitt

3
Esta es una de las cosas más divertidas que he escuchado sobre programación.)
Mert Akcakaya

22
Arithmatic también es bastante caro; deberíamos evitar los cálculos, oh, y comenzar una JVM es realmente costoso, así que no deberíamos comenzar ninguna JVM :-).
James Anderson el

Respuestas:


478

Su colega no tiene idea de qué están hablando.

Su operación más cara sería escucharlos . Perdieron su tiempo dirigiéndolo erróneamente a información que está más de una década desactualizada (a partir de la fecha original en que se publicó esta respuesta) , así como a que tuvo que pasar tiempo publicando aquí e investigando la verdad en Internet.

Afortunadamente, solo están ignorando regurgitando algo que escucharon o leyeron hace más de una década y no saben nada mejor. Tomaría cualquier otra cosa que digan como sospechosa también, esta debería ser una falacia bien conocida por cualquiera que se mantenga actualizado de cualquier manera.

Todo es un objeto (excepto primitives)

Todo lo que no sean primitivas ( int, long, double, etc.) son objetos en Java. No hay forma de evitar la creación de objetos en Java.

La creación de objetos en Java debido a sus estrategias de asignación de memoria es más rápida que C ++ en la mayoría de los casos y para todos los propósitos prácticos en comparación con todo lo demás en la JVM puede considerarse "libre" .

A principios de la década de 1990, a principios de la década de 2000, las implementaciones de JVM tenían cierta sobrecarga de rendimiento en la asignación real de objetos. Este no ha sido el caso desde al menos 2005.

Si sintoniza -Xmspara admitir toda la memoria que necesita para que su aplicación se ejecute correctamente, es posible que el GC nunca tenga que ejecutarse y barrer la mayor parte de la basura en las implementaciones modernas del GC, los programas de corta duración nunca pueden funcionar.

No intenta maximizar el espacio libre, que de todos modos es un arenque rojo, maximiza el rendimiento del tiempo de ejecución. Si eso significa que el JVM Heap está casi 100% asignado todo el tiempo, que así sea. La memoria de almacenamiento dinámico de JVM gratis no te da nada solo estar allí sentado de todos modos.

Existe la idea errónea de que el GC liberará la memoria al resto del sistema de una manera útil, ¡esto es completamente falso!

El montón de JVM no crece ni se contrae, de modo que el resto del sistema se ve afectado positivamente por la memoria libre en el montón de JVM . -Xmsasigna TODO lo que se especifica al inicio y su característica heurística es que nunca volverá a liberar nada de esa memoria al SO para compartirla con ningún otro proceso del SO hasta que esa instancia de la JVM se cierre por completo. -Xms=1GB -Xmx=1GBasigna 1 GB de RAM independientemente de cuántos objetos se creen realmente en un momento dado. Hay algunas configuraciones que permiten liberar porcentajes de la memoria de almacenamiento dinámico, pero a todos los efectos prácticos, la JVM nunca puede liberar suficiente memoria para que esto suceda.así que ningún otro proceso puede recuperar esta memoria, por lo que el resto del sistema tampoco se beneficia de que JVM Heap sea libre. Un RFE para esto fue "aceptado" el 29-NOV-2006, pero nunca se ha hecho nada al respecto. Este comportamiento no es considerado una preocupación por nadie de autoridad.

Existe la idea errónea de que la creación de muchos objetos pequeños de corta duración hace que la JVM haga una pausa durante largos períodos de tiempo, esto ahora también es falso

Los algoritmos actuales de GC están realmente optimizados para crear muchos objetos pequeños de corta duración, que es básicamente el 99% heurístico para objetos Java en cada programa. Los intentos de Agrupación de objetos en realidad harán que la JVM funcione peor en la mayoría de los casos.

Los únicos objetos que necesitan agruparse hoy son objetos que se refieren a recursos finitos que son externos a la JVM; Sockets, archivos, conexiones de bases de datos, etc. y se pueden reutilizar. Los objetos normales no se pueden agrupar en el mismo sentido que en los idiomas que le permiten acceder directamente a ubicaciones de memoria. El almacenamiento en caché de objetos es un concepto diferente y puede o no ser lo que algunas personas ingenuamente llaman agrupación , los dos conceptos no son lo mismo y no deben combinarse.

Los algoritmos de GC modernos no tienen este problema porque no se desasignan en un horario, se desasignan cuando se necesita memoria libre en una determinada generación. Si el montón es lo suficientemente grande, entonces no se producen desasignaciones el tiempo suficiente como para causar pausas.

Los lenguajes dinámicos orientados a objetos están superando a C incluso hoy en día en pruebas sensibles al cálculo.


275
+1: "Tu operación más cara sería escucharlos ...". Lo mejor que he escuchado por algún tiempo.
rjnilsson

21
@DeadMG, incluso con la sobrecarga acumulativa de GC, Java puede ser más rápido que C ++ (por ejemplo, debido a la compactación del montón, minimizando las fallas de caché para ciertas estructuras de datos).
SK-logic

13
@ SK-logic: como el GC no es determinista, en el mejor de los casos es extremadamente difícil demostrarlo. En cuanto a minimizar las pérdidas de caché, es difícil hacerlo cuando cada objeto debe ser otra indirección, desperdiciando espacio en caché y aumentando el tiempo de ejecución. Sin embargo, afirmo que simplemente usando un asignador apropiado en C ++ como un grupo de objetos o un campo de memoria, puede igualar o vencer fácilmente el rendimiento del recolector de basura. Por ejemplo, puede escribir una clase de arena de memoria que tendrá un rendimiento más rápido que _allocaamortizado.
DeadMG

33
La creación de objetos ahora es más barata de lo que solía ser. Sin embargo, no es gratis. Cualquiera que te diga que está mintiendo. Y el enlace sobre los lenguajes OO que superan a C es una respuesta exagerada a alguien que realmente está tratando de aprender.
jasonk

17
Es por este tipo de respuestas que terminamos con un código malo. La respuesta correcta es crear un objeto en Java, tanto crear el objeto Java como inicializarlo. La primera parte es barata, la segunda puede ser muy costosa. La gente siempre debe mirar lo que sucede en los constructores antes de usar la newpalabra clave en un lugar de conexión. He visto a personas usar new ImageIcon(Image)el paint()método de objetos Swing, que es bastante costoso y estaba haciendo que toda la interfaz de usuario fuera muy lenta. Por lo tanto, no es una respuesta en blanco y negro, piense antes de usar en newalgún lugar.
qwertzguy

94

En pocas palabras: no comprometa su diseño para tomar atajos creando objetos. Evite crear objetos innecesariamente. Si es razonable, diseñe para evitar operaciones redundantes (de cualquier tipo).

Contrariamente a la mayoría de las respuestas, sí, la asignación de objetos TIENE un costo asociado. Es un costo bajo, pero debe evitar crear objetos innecesarios. Igual que debe evitar cualquier cosa innecesaria en su código. Los gráficos de objetos grandes hacen que el GC sea más lento, lo que implica tiempos de ejecución más largos, ya que probablemente tendrá más llamadas a métodos, desencadenará más errores de caché de CPU y aumentará la probabilidad de que su proceso se cambie al disco en casos de poca RAM.

Antes de que nadie diga que esto es un caso marginal, he perfilado aplicaciones que, antes de la optimización, crearon más de 20 MB de objetos para procesar ~ 50 filas de datos. Esto está bien en las pruebas, hasta que escala hasta cien solicitudes por minuto y de repente está creando 2 GB de datos por minuto. Si desea hacer 20 requisitos / segundo, está creando 400 MB de objetos y luego los está tirando. 20 reqs / sec es pequeño para un servidor decente.


77
Puedo agregar un ejemplo: en algunos casos, realmente no hace ninguna diferencia en términos de claridad del código, pero puede hacer una diferencia más o menos grande en el rendimiento: al leer desde transmisiones, por ejemplo, no es raro usar algo como lo while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...que puede ser un desperdicio en comparación con, por ejemplo byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ....
JimmyB

44
+1: La creación innecesaria de objetos (o más bien la limpieza después de la eliminación) definitivamente a veces puede ser costosa. El código 3D Graphics / OpenGL en Java es un lugar donde he visto optimizaciones para minimizar la cantidad de objetos creados, ya que GC puede causar estragos en su velocidad de fotogramas.
Leo

1
¡Guauu! ¿Más de 20 MB para procesar 50 filas de datos? Suena loco. En cualquier caso, ¿son esos objetos longevos? Porque ese es el caso que importaría para GC. Si, por otro lado, simplemente está discutiendo los requisitos de memoria , eso no está relacionado con la recolección de basura o la eficiencia de la creación de objetos ...
Andres F.

1
Los objetos son de corta duración (menos de 0.5 segundos en el caso general). Ese volumen de basura todavía afecta el rendimiento.
jasonk

2
Gracias por mantenerse firme en la realidad, cualquiera que viva con los efectos de una arquitectura irreflexiva puede relacionarse mucho.
JM Becker

60

En realidad, debido a las estrategias de administración de memoria que hace posible el lenguaje Java (o cualquier otro lenguaje administrado), la creación de objetos es poco más que incrementar un puntero en un bloque de memoria llamado generación joven. Es mucho más rápido que C, donde hay que buscar memoria libre.

La otra parte del costo es la destrucción de objetos, pero es difícil de comparar con C. El costo de una colección se basa en la cantidad de objetos guardados a largo plazo, pero la frecuencia de las colecciones se basa en la cantidad de objetos creados ... Al final, sigue siendo mucho más rápido que la administración de memoria de estilo C.


77
+1 Aunque el recolector de basura "simplemente funciona", cada programador de Java debe aprender sobre el recolector de basura generacional.
benzado

18
Las versiones más recientes de Java pueden hacer análisis de escape, lo que significa que puede asignar memoria para objetos que no escapan a un método en la pila, de modo que limpiarlos es gratis: el recolector de basura no tiene que lidiar con esos objetos , se descartan automáticamente cuando el marco de pila del método se desenrolla (cuando el método regresa).
Jesper

44
El siguiente paso para el análisis de escape es "asignar" memoria para objetos pequeños únicamente en registros (por ejemplo, un Pointobjeto podría caber en 2 registros de propósito general).
Joachim Sauer

66
@Joachim Sauer: Eso es lo que se hace en las implementaciones más recientes de HotSpot VM. Se llama reemplazo escalar.
someguy

1
@someguy: lo leí hace algún tiempo como lo siguiente, pero no hice un seguimiento para verificar si ya está hecho. Es una excelente noticia saber que ya tenemos esto.
Joachim Sauer

38

Otros carteles han señalado correctamente que la creación de objetos es extremadamente rápida en Java, y que no debe preocuparse por ello en ninguna aplicación Java normal.

Hay un par de situaciones muy especiales en las que es una buena idea evitar la creación de objetos.

  • Cuando está escribiendo una aplicación sensible a la latencia y desea evitar las pausas de GC. Cuantos más objetos produzca, más GC ocurrirá y mayor será la posibilidad de pausas. Esta podría ser una consideración válida para los juegos relevantes, algunas aplicaciones de medios, control robótico, comercio de alta frecuencia, etc. La solución es preasignar todos los objetos / matrices que necesita por adelantado y reutilizarlos. Hay bibliotecas que se especializan en proporcionar este tipo de capacidad, por ejemplo, Javolution . Pero podría decirse que si realmente le importa la baja latencia, debería usar C / C ++ / assembler en lugar de Java o C # :-)
  • Evitar las primitivas en caja (Doble, Entero, etc.) puede ser una micro optimización muy beneficiosa en ciertas circunstancias. Dado que las primitivas sin caja (double, int, etc.) evitan la sobrecarga por objeto, son mucho más rápidas en el trabajo intensivo de la CPU, como el procesamiento numérico, etc. Por lo general, las matrices primitivas funcionan muy bien en Java, por lo que desea usarlas para su cálculo de números en lugar de Cualquier otro tipo de objetos.
  • En situaciones de memoria restringida , desea minimizar la cantidad de objetos (activos) creados, ya que cada objeto conlleva una pequeña sobrecarga (generalmente 8-16 bytes dependiendo de la implementación de JVM). En tales circunstancias, debe preferir una pequeña cantidad de objetos o matrices grandes para almacenar sus datos en lugar de una gran cantidad de objetos pequeños.

3
Vale la pena señalar que el análisis de escape permitirá que los objetos de corta duración (vida limitada) se almacenen en la pila, estos objetos se pueden desasignar gratis ibm.com/developerworks/java/library/j-jtp09275/index.html
Richard Tingle

1
@ Richard: gran punto: el análisis de escape proporciona algunas muy buenas optimizaciones. Sin embargo, debe tener cuidado al confiar en él: no se garantiza que suceda en todas las implementaciones de Java y en todas las circunstancias. Por lo tanto, a menudo necesita un punto de referencia para estar seguro.
mikera

2
Además, el análisis de escape 100% correcto es equivalente al problema de detención. No confíe en el análisis de escape en situaciones complejas, ya que es probable que dé una respuesta incorrecta al menos algunas veces.
Julio

El primer y último punto no se aplican a objetos pequeños que se liberan rápidamente, mueren en eden (piense en la asignación de la pila en C, más o menos gratis) y nunca afectan los tiempos de GC o la asignación de memoria. La mayor parte de la asignación de objetos en Java cae en esta categoría y, por lo tanto, es esencialmente gratuita. El segundo punto sigue siendo válido.
Bill K

17

Hay un núcleo de verdad en lo que dice tu colega. Sugiero respetuosamente el tema con objeto de creación es en realidad la basura recogida . En C ++, el programador puede controlar con precisión cómo se desasigna la memoria. El programa puede acumular suciedad por el tiempo que sea necesario. Además, el programa C ++ puede descartar el error usando un hilo diferente al que lo creó. Por lo tanto, el hilo que funciona actualmente nunca tiene que detenerse para limpiar.

Por el contrario, la máquina virtual Java (JVM) detiene periódicamente su código para recuperar la memoria no utilizada. La mayoría de los desarrolladores de Java nunca notan esta pausa, ya que generalmente es poco frecuente y muy corta. Cuanto más crud acumule o más limitada sea su JVM, más frecuentes serán estas pausas. Puede usar herramientas como VisualVM para visualizar este proceso.

En versiones recientes de Java, se puede ajustar el algoritmo de recolección de basura (GC) . Como regla general, cuanto más corto desee hacer las pausas, más costosa será la sobrecarga en la máquina virtual (es decir, CPU y memoria dedicadas a coordinar el proceso de GC).

¿Cuándo podría importar esto? Cada vez que le interesen las tasas de respuesta consistentes por debajo de los milisegundos, se preocupará por GC. Los sistemas comerciales automatizados escritos en Java ajustan mucho la JVM para minimizar las pausas. Las empresas que de otro modo escribirían Java recurren a C ++ en situaciones en las que los sistemas tienen que ser muy receptivos todo el tiempo.

¡Para el registro, no apruebo la evitación de objetos en general! Por defecto a la programación orientada a objetos. Ajuste este enfoque solo si el GC se interpone en su camino y luego solo después de intentar ajustar la JVM para que se detenga por menos tiempo. Un buen libro sobre ajuste de rendimiento de Java es Java Performance de Charlie Hunt y Binu John.


10
La mayoría de las JVM modernas admiten mejores algoritmos de recolección de basura que "detener el mundo". El tiempo amortizado dedicado a la recolección de basura es con frecuencia menor que lo que se dedica explícitamente a llamar a malloc / free. ¿La administración de memoria de C ++ también se ejecuta en un hilo separado?

10
Las versiones más recientes de Java de Oracle pueden hacer análisis de escape, lo que significa que los objetos que no escapan a un método se asignan en la pila, lo que hace que la limpieza sea gratuita; se desasignan automáticamente cuando el método regresa.
Jesper

2
@ ThorbjørnRavnAndersen: ¿En serio? ¿Realmente quiere decir GC sin pausa , o se refiere a "generalmente-paralelo-pero-a veces-un-pequeño-bit-pausa" GC? No entiendo cómo el GC nunca puede pausar un programa, y ​​aún así mover objetos al mismo tiempo ... parece, literalmente, imposible para mí ...
Mehrdad

2
@ ThorbjørnRavnAndersen: ¿Cómo puede "detener el mundo" pero nunca "detener la JVM"? Eso no tiene sentido ...
Mehrdad

2
@ ThorbjørnRavnAndersen: No, realmente no; Simplemente no entiendo lo que estás diciendo. O el GC a veces detiene el programa, en cuyo caso, por definición, no es "sin pausa", o nunca detiene el programa (por lo tanto, es "sin pausa"), en cuyo caso no entiendo cómo corresponde a su declaración "detiene el mundo cuando no puede seguir el ritmo", ya que parece implicar que el programa está en pausa mientras el GC está funcionando. ¿Te importaría explicar cuál es el caso (¿es sin pausa o no?) Y cómo es posible?
Mehrdad


9

el GC está sintonizado para muchos objetos de corta duración

Dicho esto, si puedes reducir trivialmente la asignación de objetos, deberías

un ejemplo sería construir una cadena en un bucle, la forma ingenua sería

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

que crea un nuevo Stringobjeto en cada +=operación (más StringBuilderay la nueva matriz de caracteres subyacente)

puedes reescribir esto fácilmente en:

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

Este patrón (resultado inmutable y un valor intermedio local mutable) también se puede aplicar a otras cosas

pero aparte de eso, debes abrir un generador de perfiles para encontrar el cuello de botella real en lugar de perseguir fantasmas


La mayor ventaja de este StringBuilderenfoque es el pre-tamaño de la StringBuildermanera que nunca tiene que reasignar la matriz subyacente con el StringBuilder(int)constructor. Esto lo convierte en una única asignación, en lugar de una 1+Nasignación.

2
@JarrodRoberson StringBuilder será al menos el doble de la capacidad de corriente o en otras palabras la capacidad crecerá de manera exponencial por sólo log (n) las asignaciones
trinquete monstruo

6

Joshua Bloch (uno de los creadores de la plataforma Java) escribió en su libro Effective Java en 2001:

Evitar la creación de objetos manteniendo su propio grupo de objetos es una mala idea a menos que los objetos en el grupo sean extremadamente pesados. Un ejemplo prototípico de un objeto que justifica un grupo de objetos es una conexión de base de datos. El costo de establecer la conexión es suficientemente alto como para que tenga sentido reutilizar estos objetos. En términos generales, sin embargo, mantener sus propios grupos de objetos satura su código, aumenta la huella de la memoria y perjudica el rendimiento. Las implementaciones modernas de JVM tienen recolectores de basura altamente optimizados que superan fácilmente dichos grupos de objetos en objetos livianos.


1
Incluso con cosas como la conexión de red, lo importante no es mantener los objetos asignados por GC asociados con las conexiones, sino mantener el conjunto de conexiones que existen fuera del mundo administrado por GC. Hay momentos en que agrupar objetos en sí mismos puede ser útil; La mayoría de ellos provienen de casos en los que un par de referencias al mismo objeto pueden ser semánticamente equivalentes a un par de referencias a objetos idénticos pero distintos, pero no obstante tienen algunas ventajas. Por ejemplo, dos referencias a la misma cadena de cincuenta mil caracteres pueden verificarse para la igualdad ...
supercat

2
... mucho más rápido que las referencias a dos cadenas distintas pero idénticas de esa longitud.
supercat

5

Esto realmente depende de la aplicación específica, por lo que es realmente difícil de decir en general. Sin embargo, me sorprendería mucho si la creación de objetos fuera realmente un cuello de botella de rendimiento en una aplicación. Incluso si son lentos, el beneficio de estilo de código probablemente superará el rendimiento (a menos que sea realmente notable para el usuario)

En cualquier caso, ni siquiera debe comenzar a preocuparse por estas cosas hasta que haya perfilado su código para determinar los cuellos de botella de rendimiento reales en lugar de adivinar. Hasta entonces, debe hacer lo que sea mejor para la legibilidad del código, no el rendimiento.


En las primeras versiones de Java, se incurría en un costo considerable cuando el recolector de basura limpiaba los objetos descartados. Las versiones recientes de Java han mejorado enormemente las opciones de GC, por lo que la creación de objetos rara vez es un problema. Por lo general, los sistemas web están limitados por llamadas de E / S a sistemas externos a menos que esté computando algo realmente complejo en el servidor de aplicaciones como parte de la carga de la página.
Greg

3

Creo que su colega debe haber dicho desde la perspectiva de la creación innecesaria de objetos. Quiero decir, si estás creando el mismo objeto con frecuencia, entonces es mejor compartir ese objeto. Incluso en los casos en que la creación de objetos es compleja y requiere más memoria, es posible que desee clonar ese objeto y evitar crear ese proceso complejo de creación de objetos (pero eso depende de sus requisitos). Creo que la afirmación "La creación de objetos es costosa" debería tomarse en contexto.

En lo que respecta a los requisitos de memoria de JVM, espere a Java 8, ni siquiera necesita especificar -Xmx, la configuración del meta espacio se ocupará de la necesidad de memoria de JVM y crecerá automáticamente.


Sería muy constructivo si la gente también comentara mientras rechaza cualquier respuesta. Después de todo, todos estamos aquí para compartir conocimientos, y simplemente juzgar sin una razón adecuada no servirá para nada.
AKS

1
El intercambio de pila debe tener algún mecanismo para notificar a los usuarios que rechazan cualquier respuesta, cuando se publica un comentario sobre esa respuesta.
AKS

1

Crear una clase es más que asignar memoria. También hay inicialización, no estoy seguro de por qué esa parte no está cubierta por todas las respuestas. Las clases normales contienen algunas variables y realizan alguna forma de inicialización, y eso no es gratis. Dependiendo de cuál sea la clase, puede leer archivos o realizar cualquier otra cantidad de operaciones lentas.

Tan solo considere lo que hace el constructor de la clase, antes de calcular si es gratis o no.


2
Una clase bien diseñada no debería hacer trabajo pesado en su constructor, solo por esta razón. Por ejemplo, su clase de lectura de archivos podría reducir su costo de creación de instancias solo verificando que su archivo de destino existe en el inicio y aplazando cualquier operación real del archivo hasta la primera llamada al método que necesita datos del archivo.
GordonM

2
Si el costo adicional se mueve al 'if (firstTime)', recrear la clase en un ciclo es aún más costoso que reutilizar la clase.
Alex

Estaba a punto de publicar una respuesta similar, y creo que esta es la respuesta correcta. Esas personas están cegadas por esos dichos de que crear objetos Java es barato que no se dan cuenta de que a menudo los constructores o la inicialización adicional pueden estar lejos de ser baratos. Por ejemplo, la clase ImageIcon en Swing, incluso si le pasa un objeto Image precargado, el constructor es bastante costoso. Y no estoy de acuerdo con @GordonM, muchas clases del JDK realizan la mayor parte del init en el constructor, y creo que hace que el código sea más ágil, mejor diseñado.
qwertzguy

1

El GC de Java en realidad está muy optimizado en términos de crear muchos objetos rápidamente de manera "explosiva". Por lo que puedo entender, usan un asignador secuencial (el asignador O (1) más rápido y simple para solicitudes de tamaño variable) para ese tipo de "ciclo de ráfaga" en un espacio de memoria que llaman "espacio Eden", y solo si los objetos persisten después de un ciclo de GC, ¿se trasladan a un lugar donde el GC puede recogerlos uno por uno?

Dicho esto, si sus necesidades de rendimiento se vuelven lo suficientemente críticas (como se mide con los requisitos reales del usuario final), entonces los objetos conllevan una sobrecarga, pero no pensaría tanto en términos de creación / asignación. Tiene más que ver con la localidad de referencia y el tamaño adicional de todos los objetos en Java, según sea necesario para admitir conceptos como la reflexión y el despacho dinámico ( Floates más grande float, a menudo algo así como 4 veces más grande en 64 bits con sus requisitos de alineación, y un Floatno se garantiza necesariamente que la matriz se almacene contigua a lo que entiendo).

Una de las cosas más llamativas que vi desarrollar en Java que me hizo considerarlo un contendiente de peso pesado en mi campo (VFX) fue un trazado de ruta estándar interactivo y multiproceso (que no usa almacenamiento en caché de irradiancia o BDPT o MLS o cualquier otra cosa) en La CPU proporciona vistas previas en tiempo real que convergen bastante rápido a una imagen libre de ruido. He trabajado con profesionales en C ++ dedicando sus carreras a tales cosas con perfiladores sofisticados que tenían dificultades para hacerlo.

Pero eché un vistazo al código fuente y, si bien utilizaba muchos objetos a un costo trivial, las partes más críticas del trazado de ruta (el BVH y los triángulos y materiales) evitaban de manera clara y deliberada los objetos a favor de grandes conjuntos de tipos primitivos ( en su mayoría float[]y int[]), lo que hizo que usara mucho menos memoria y garantizara la ubicación espacial para pasar de una floaten la matriz a la siguiente. No creo que sea demasiado especulativo pensar que si el autor usara tipos en caja comoFloatallí, habría tenido un costo bastante alto para su rendimiento. Pero estamos hablando de la parte más absolutamente crítica de ese motor, y estoy bastante seguro, dada la habilidad con la que el desarrollador lo optimizó, que lo midió y aplicó esa optimización de manera muy juiciosa, ya que felizmente usaba objetos en todas partes con costo trivial para su impresionante trazado de ruta en tiempo real.

Un colega me dijo que en Java la creación de objetos es la operación más costosa que podría realizar. Así que solo puedo concluir para crear la menor cantidad de objetos posible.

Incluso en un campo tan crítico para el rendimiento como el mío, no va a escribir productos eficientes si se estira los músculos isquiotibiales en lugares que no importan. Incluso afirmaría que los campos más críticos para el rendimiento podrían generar una demanda aún mayor de productividad, ya que necesitamos todo el tiempo extra que podamos sintonizar esos puntos críticos que realmente importan al no perder tanto tiempo en cosas que no . Al igual que con el ejemplo de trazado de ruta de arriba, el autor aplicó hábil y juiciosamente tales optimizaciones solo a los lugares que realmente importaban, y probablemente en retrospectiva después de medir, y todavía utilizaba felizmente objetos en cualquier otro lugar.


1
Tu primer párrafo está en el camino correcto. Los espacios edén y joven son coleccionistas de copias que tienen aprox. 0 costo por objetos muertos. Le recomiendo esta presentación . En una nota al margen, te das cuenta de que esta pregunta es de 2012, ¿verdad?
JimmyJames

@JimmyJames Me aburro y me gusta examinar las preguntas, incluidas las antiguas. ¡Espero que a la gente no le importe mi nigromancia! :-D
Dragon Energy

@JimmyJames ¿Ya no es el caso de que los tipos en caja tengan requisitos de memoria adicionales y no se garantice que sean contiguos en una matriz? Tiendo a pensar que los objetos son muy baratos en Java, pero el caso en el que podrían ser relativamente caros es como float[]vs. Float[], donde el procesamiento de un millón de los primeros secuencialmente podría ser relativamente más rápido que el segundo.
Dragon Energy

1
Cuando comencé a publicar aquí, hice lo mismo. Simplemente no se sorprenda cuando las respuestas a viejas preguntas tienden a recibir muy poca atención.
JimmyJames

1
Estoy bastante seguro de que se debe suponer que los tipos en caja usan más espacio que las primitivas. Hay posibles optimizaciones en torno a eso, pero en general, creo que es como usted describe. Gil Tene (de la presentación anterior) tiene otra charla sobre una propuesta para agregar un tipo especial de colección que proporcionaría algunos de los beneficios de las estructuras pero que todavía usan objetos. Es una idea interesante, no estoy seguro del estado de la JSR. El costo se debe en gran parte al tiempo, que es a lo que usted alude. Si puede evitar dejar que sus objetos 'escapen', incluso se les puede asignar una pila y nunca tocar el montón.
JimmyJames

0

Como la gente ha dicho, la creación de objetos no es un gran costo en Java (pero apuesto a que es más grande que la mayoría de las operaciones simples como agregar, etc.) y no debe evitarlo demasiado.

Dicho esto, sigue siendo un costo y, a veces, es posible que intentes desechar tantos objetos como sea posible. Pero solo después de que el perfil haya demostrado que esto es un problema.

Aquí hay una gran presentación sobre el tema: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf


0

Hice un microbenchmark rápido con respecto a esto y proporcioné fuentes completas en github. Mi conclusión es que si crear objetos es costoso o no, no es el problema, pero crear objetos continuamente con la noción de que el GC se encargará de usted hará que su aplicación active el proceso del GC antes. El GC es un proceso muy costoso y es mejor evitarlo siempre que sea posible y no tratar de presionarlo para que entre en acción.


Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las 15 respuestas anteriores. Apenas un contenido digno chocar una pregunta que se hizo hace más de 2 años y ha conseguido una respuesta muy cuidadosa
mosquito
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.