¿Debemos evitar la creación de objetos en Java?

230

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

Esto parece en cierto modo anular el propósito de la programación orientada a objetos. Si no estamos creando objetos, simplemente estamos escribiendo un estilo de clase C largo, para la optimización?

    
pregunta Slamice 22.05.2012 - 04:06

14 respuestas

455

Su colega no tiene idea de lo que están hablando.

Su operación más costosa sería escucharlos . Perdieron su tiempo dirigiéndolo a información que está desactualizada hace más de una década (a partir de la fecha original en que se publicó esta respuesta) , además de tener que dedicar tiempo a publicar aquí e investigar en Internet. por la verdad

Esperemos que solo estén regurgitando ignorantemente algo que escucharon o leyeron hace más de una década y no sepan nada mejor. Tomaría cualquier otra cosa que digan como sospechosa también, esto debería ser una falacia bien conocida por cualquier persona que se mantenga actualizada de cualquier manera.

Todo es un objeto (excepto primitives )

Todo lo que no sea primitivos ( int, long, double , etc.) son Objetos en Java. No hay manera 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 los 90, 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 el 2005.

Si ajusta -Xms para que sea compatible con toda la memoria que necesita para que su aplicación se ejecute correctamente, es posible que el GC nunca tenga que ejecutar y barrer la mayor parte de la basura en las implementaciones modernas de GC, los programas de corta duración nunca podrán hacerlo. .

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

Hay una 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 y se contrae, por lo que el resto del sistema se ve afectado positivamente por la memoria libre en el montón de JVM . -Xms asigna TODO lo que se especifica en el inicio y su heurística es nunca devolver realmente ninguna parte de esa memoria al sistema operativo para compartirla con ningún otro proceso del sistema operativo hasta que la instancia de la JVM se cierre por completo. -Xms=1GB -Xmx=1GB asigna 1 GB de RAM independientemente de cuántos objetos se creen realmente en un momento dado. Hay algunas configuraciones que permiten que se liberen porcentajes de la memoria del montón, pero para todos los propósitos prácticos la JVM nunca puede liberar suficiente memoria para que esto ocurra , por lo que ningún otro proceso puede reclamar esta memoria, por lo que el resto del sistema tampoco se beneficia de que la JVM Heap sea gratuita . Una RFE para esto fue "aceptada" 29-NOV-2006, pero nunca se ha hecho nada al respecto. Este comportamiento no es considerado una preocupación por ninguna persona con autoridad.

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

Los algoritmos GC actuales en realidad están optimizados para crear muchos objetos pequeños de corta duración, que es básicamente la heurística del 99% 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 agrupación hoy son Objetos que se refieren a recursos finitos que son externos para la JVM; Sockets, archivos, conexiones de base de datos, etc. y pueden ser reutilizados. Los objetos normales no se pueden agrupar en el mismo sentido que en los idiomas que le permiten acceder directamente a las ubicaciones de la memoria. El almacenamiento en caché de objetos es un concepto diferente y puede o no ser lo que algunas personas llaman ingenuamente agrupamiento , los dos conceptos no son lo mismo y no deben combinarse.

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

Los lenguajes dinámicos orientados a objetos están superando a C incluso hoy en día en datos confidenciales pruebas.

    
respondido por el Jarrod Roberson 22.05.2012 - 04:33
86

Línea inferior: No comprometa su diseño para tomar atajos creando objetos. Evite crear objetos innecesariamente. Si es sensato, 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 debes evitar cualquier cosa innecesaria en tu código. Los gráficos de objetos grandes hacen que el GC sea más lento, implican tiempos de ejecución más largos, ya que probablemente tendrá más llamadas a métodos, desencadenará más fallos de caché de CPU y aumentará la probabilidad de que su proceso se cambie al disco en casos de poca RAM.

Antes de que alguien diga que este es un caso de ventaja, he perfilado aplicaciones que, antes de optimizar, crearon más de 20 MB de objetos para procesar ~ 50 filas de datos. Esto está bien en las pruebas, hasta que aumente hasta cien solicitudes por minuto y de repente esté creando 2GB de datos por minuto. Si quieres hacer 20 requisitos por segundo, estás creando 400 MB de objetos y luego los desechas. 20 reqs / sec es minúsculo para un servidor decente.

    
respondido por el jasonk 22.05.2012 - 04:41
60

En realidad, debido a las estrategias de administración de memoria que el lenguaje Java (o cualquier otro lenguaje administrado) hace posible, 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 se debe realizar una búsqueda de 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.

    
respondido por el amara 22.05.2012 - 04:20
38

Otros carteles han señalado con razón que la creación de objetos es extremadamente rápida en Java, y que normalmente no debería preocuparse por eso en ninguna aplicación Java normal.

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

  • Cuando escribe una aplicación sensible a la latencia y desea evitar las pausas de GC. Cuantos más objetos produzca, más GC sucederá y mayor será la posibilidad de pausas. Esta podría ser una consideración válida para juegos, aplicaciones de medios, control robótico, comercio de alta frecuencia, etc. La solución es asignar previamente todos los objetos / arrays 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 te importa la baja latencia, deberías usar C / C ++ / assembler en lugar de Java o C # :-)
  • Evitar primitivas en caja (doble, entero, etc.) puede ser una microoptimización muy beneficiosa en ciertas circunstancias. Dado que las primitivas sin caja (double, int, etc.) evitan la sobrecarga por objeto, son un trabajo intensivo de la CPU mucho más rápido, como el procesamiento numérico, etc. Por lo general, las matrices primitivas funcionan muy bien en Java, por lo que debe usarlas para procesar sus números en lugar de cualquier otro tipo de objetos.
  • En situaciones de memoria restringida desea minimizar el número de objetos (activos) creados, ya que cada objeto conlleva una pequeña sobrecarga (generalmente de 8 a 16 bytes según la implementación de la JVM). En tales circunstancias, debería preferir una pequeña cantidad de objetos grandes o matrices para almacenar sus datos en lugar de una gran cantidad de objetos pequeños.
respondido por el mikera 22.05.2012 - 13:17
17

Hay un núcleo de verdad en lo que dice tu colega. Respetuosamente sugiero que el problema con el objeto creación es en realidad la colección . En C ++, el programador puede controlar con precisión cómo se desasigna la memoria. El programa puede acumular crud durante tanto tiempo o tan corto como quiera. Además, el programa C ++ puede descartar el material utilizando un hilo diferente al que lo creó. Por lo tanto, el hilo que está trabajando actualmente nunca tiene que detenerse para limpiar.

En contraste, la Máquina Virtual Java (JVM) detiene periódicamente su código para reclamar la memoria no utilizada. La mayoría de los desarrolladores de Java nunca notan esta pausa, ya que suele ser poco frecuente y muy breve. Cuanto más crud se acumule o más restringida esté su JVM, más frecuentes serán estas pausas. Puede utilizar herramientas como VisualVM para visualizar este proceso.

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

¿Cuándo podría esto importar? Cada vez que te preocupes por tasas de respuesta constantes de menos de milisegundos, te importará GC. Los sistemas comerciales automatizados escritos en Java ajustan la JVM para minimizar las pausas. Las empresas que de otro modo escribirían Java se dirigen a C ++ en situaciones donde los sistemas tienen que ser altamente receptivos todo el tiempo.

Para el registro, yo no aprobamos la evitación de objetos en general. Predeterminado a la programación orientada a objetos. Ajuste este enfoque solo si el GC se interpone en su camino y solo después de intentar ajustar la JVM para pausar por menos tiempo. Un buen libro sobre la optimización del rendimiento de Java es Java Performance de Charlie Hunt y Binu John.

    
respondido por el greg 22.05.2012 - 05:34
11

Hay un caso en el que se puede desanimar a crear demasiados objetos en Java debido a la sobrecarga: diseño para el rendimiento en la plataforma Android

Aparte de eso, las respuestas anteriores son verdaderas.

    
respondido por el Peter Kelly 22.05.2012 - 10:30
8

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

dice que 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 objeto String en cada operación += (más un StringBuilder y 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 mutable local) también se puede aplicar a otras cosas

pero aparte de eso, deberías abrir un generador de perfiles para encontrar el verdadero cuello de botella en lugar de perseguir fantasmas

    
respondido por el ratchet freak 22.05.2012 - 10:43
5

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

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

    
respondido por el Oleksi 22.05.2012 - 04:08
5

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 un mal   Idea a menos que los objetos en la piscina sean extremadamente pesados. UNA   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   lo suficientemente alto como para que tenga sentido reutilizar estos objetos.   En general, sin embargo, manteniendo sus propios grupos de objetos.   desordena tu código, aumenta la huella de memoria y daña   actuación. Las implementaciones modernas de JVM han optimizado la basura.   coleccionistas que superan fácilmente tales grupos de objetos en peso ligero   objetos.

    
respondido por el Anthony Ananich 03.10.2014 - 19:23
3

Creo que su colega debe haber dicho desde la perspectiva de la creación innecesaria de objetos. Quiero decir que si está creando el mismo objeto con frecuencia, 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 declaració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 Java 8, ni siquiera necesita especificar -Xmx, la configuración del espacio meta se ocupará de la necesidad de memoria de JVM y crecerá automáticamente.

    
respondido por el AKS 01.08.2013 - 23:48
1

Crear una clase es más que asignar memoria. También está la inicialización, no estoy seguro de por qué esa parte no está cubierta por todas las respuestas. Las clases normales contienen algunas variables y hacen algún tipo de inicialización, y eso no es gratis. Dependiendo de lo que sea la clase, puede leer archivos o hacer cualquier otra cantidad de operaciones lentas.

Entonces, considera lo que hace el constructor de la clase, antes de averiguar si es gratis o no.

    
respondido por el Alex 22.05.2012 - 23:59
1

El GC de Java en realidad está muy optimizado en términos de crear muchos objetos rápidamente en forma de "ráfaga". Por lo que puedo entender, utilizan un asignador secuencial (el asignador O (1) más rápido y más simple para solicitudes de tamaño variable) para ese tipo de "ciclo de ráfaga" en un espacio de memoria al que llaman "espacio Eden", y solo si los objetos persisten. después de un ciclo de GC, se mueven a un lugar donde el GC puede recolectarlos 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 lo pensaría mucho 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 envío dinámico ( Float es más grande que float , a menudo algo así como 4 veces más grande en 64 bits con sus requisitos de alineación, y una matriz de Float no está necesariamente garantizada para ser almacenada contigua a lo que entiendo).

Una de las cosas más llamativas que he visto desarrollada en Java que me hizo considerarlo un contendiente de peso pesado en mi campo (VFX) era un trazador de ruta estándar, multiproceso e interactivo (sin usar el almacenamiento en caché por irradiación o BDPT o MLS o algo así) else) en la CPU que proporciona vistas previas en tiempo real que convergen bastante rápidamente a una imagen sin ruido. He trabajado con profesionales en C ++ dedicando sus carreras a esas cosas con elaboradores de perfiles en la mano que tuvieron dificultades para hacerlo.

Pero observé el código fuente y, aunque usaba muchos objetos a un costo trivial, las partes más críticas del trazador de ruta (BVH y triángulos y materiales) evitaban los objetos de forma muy clara y deliberada en favor de grandes arreglos de tipos primitivos (en su mayoría float[] y int[] ), lo que hizo que usara mucha menos memoria y garantizase que la localidad espacial pasara de un float en la matriz a la siguiente. No creo que sea demasiado especulativo pensar que, si el autor usara tipos encajonados como Float allí, habría tenido un costo bastante grande para su desempeño. Pero estamos hablando de la parte más absolutamente crítica de ese motor, y estoy bastante seguro, dado lo hábilmente que el desarrollador lo optimizó, que midió eso y aplicó esa optimización muy, muy juiciosamente, ya que usó objetos en cualquier otro lugar con costo trivial para su impresionante trazador 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ías realizar. Así que solo puedo concluir para crear la menor cantidad de objetos posible.

  

Incluso en un campo tan crítico como el mío, no escribirá productos eficientes si se enreda en lugares que no importan. Incluso me atrevería a afirmar que los campos más críticos para el rendimiento podrían generar una mayor demanda de productividad, ya que necesitamos todo el tiempo adicional que podamos para ajustar esos hotspots que realmente importan al no perder ese tiempo en cosas que no lo hacen. . Al igual que en el ejemplo anterior, el autor aplicó dichas optimizaciones de manera hábil y juiciosa solo a los lugares que verdaderamente importaban, y probablemente en retrospectiva después de la medición, y que seguían utilizando objetos en cualquier otro lugar.

    
respondido por el Dragon Energy 20.12.2018 - 23:39
0

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

Dicho esto sigue siendo un costo, y algunas veces es posible que intente eliminar 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: enlace

    
respondido por el rkj 24.05.2012 - 04:56

Lea otras preguntas en las etiquetas