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?
Respuestas:
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
-Xms
para 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 .
-Xms
asigna 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=1GB
asigna 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.
fuente
_alloca
amortizado.new
palabra clave en un lugar de conexión. He visto a personas usarnew ImageIcon(Image)
elpaint()
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 ennew
algún lugar.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.
fuente
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
que puede ser un desperdicio en comparación con, por ejemplobyte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
.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.
fuente
Point
objeto podría caber en 2 registros de propósito general).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.
fuente
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.
fuente
Hay un caso en el que puede desanimarse de crear demasiados objetos en Java debido a la sobrecarga: el diseño para el rendimiento en la plataforma Android
Aparte de eso, las respuestas anteriores son ciertas.
fuente
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
que crea un nuevo
String
objeto en cada+=
operación (másStringBuilder
ay la nueva matriz de caracteres subyacente)puedes reescribir esto fácilmente en:
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
fuente
StringBuilder
enfoque es el pre-tamaño de laStringBuilder
manera que nunca tiene que reasignar la matriz subyacente con elStringBuilder(int)
constructor. Esto lo convierte en una única asignación, en lugar de una1+N
asignación.Joshua Bloch (uno de los creadores de la plataforma Java) escribió en su libro Effective Java en 2001:
fuente
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.
fuente
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.
fuente
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.
fuente
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 (
Float
es más grandefloat
, a menudo algo así como 4 veces más grande en 64 bits con sus requisitos de alineación, y unFloat
no 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[]
yint[]
), lo que hizo que usara mucho menos memoria y garantizara la ubicación espacial para pasar de unafloat
en la matriz a la siguiente. No creo que sea demasiado especulativo pensar que si el autor usara tipos en caja comoFloat
allí, 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.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.
fuente
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.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
fuente
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.
fuente