[vc_row][vc_column][vc_column_text]
La mayoría de los desarrolladores esperan que la optimización de rendimiento sea un tema complicado que requiere mucha experiencia y conocimiento. No están equivocados, del todo. Optimizar una aplicación para obtener le mejor rendimiento posible no es una tarea fácil. Pero eso no implica que no puedas hacer nada si no has adquirido este conocimiento. Hay varias recomendaciones fáciles de seguir y mejores prácticas que lo ayuden a crear una aplicación que funcione bien.
La mayoría de estas recomendaciones son específicas de Java. Pero hay varias recomendaciones independientes del lenguaje de programación. Empecemos por las recomendaciones genéricas y luego pasamos a las recomendaciones propias del lenguaje.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
1. No optimizar antes de saber que es necesario
Para empezar, debe seguir las mejores prácticas comunes e intentar implementar sus casos de uso de manera eficiente. Pero esto no significa que deba reemplazar cualquier librería estandard o crear optimizaciones complejas antes de demostrar que es necesario.
En la mayoría de los casos, la optimización prematura ocupa mucho tiempo y hace que el código sea difícil de leer y mantener. Y para empeorar las cosas, estas optimizaciones a menudo no brindan ningún beneficio por que está interviniendo mucho tiempo optimizando partes no críticas de la aplicación.
Entonces, ¿Cómo demuestras que necesitas optimizar algo?
En primer lugar, debe definir que tan rápido debe ser el código de su aplicación, por ejemplo, especificando un tiempo máximo de respuesta para todas las llamadas o la cantidad de registros que desea importar en un marco de tiempo. Después de esto puedes medir que partes de la aplicación son demasiado lentas y necesitas mejorar.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
2. Utiliza Profiler para encontrar el verdadero cuello de botella
Después de seguir la primera recomendación e identificar las partes de su aplicación que necesita mejorar, pregúntese por dónde empezar.
Puedes abordar esta pregunta de dos maneras:
- Puedes echarle un vistazo al código y empezar con la parte que parece sospechosa o donde sientes que podría crear problemas.
- O utilizar Profiler y obtienes información detallada sobre el comportamiento y el rendimiento de cada parte de su código.
El método basado en el Profiler le brinda una adecuada comprensión de las implicaciones de su código y le permite enfocarse en las partes más críticas.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
3. Cree un conjunto de pruebas de rendimiento para toda la aplicación
Este es otro consejo general que le ayuda a evitar una gran cantidad de problemas inesperados que a menudo ocurren después de haber implementado su mejora de rendimiento en la producción. Siempre debe definir un conjunto de pruebas de rendimiento que pruebe toda la aplicación y ejecutarlas antes y después de que haya trabajado en una mejora de rendimiento.
Estas ejecuciones de pruebas adicionales le ayudarán a identificar los efectos secundarios funcionales y de rendimiento del cambio realizado y asegurarse de que no envíe una actualización que causó más daño que beneficio. Esto es especialmente importante si trabaja con componentes que utilizan varias partes diferentes de la aplicación, como base de datos o caché.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
4. Trabaja en los mayores cuellos de botella primero
Después de crear la suite de pruebas y analizar la aplicación con un Profiler, tiene una lista de problemas que desea abordar para mejorar el rendimiento. Eso está bien, pero todavía no responde la pregunta por la que debes comenzar. Podría centrarse en los quick wins (aquellos fáciles y ganancias rápidas) o empezar por el problema más importante.
Puede ser tentador empezar por los quick wins por que pronto podrá demostrar los primeros resultados. A veces, eso podría ser necesario para convencer a otros miembros del equipo o a la gerencia de que el análisis de desempeño valió la pena.
Pero, en general, se recomienda comenzar por los grandes problemas o el problema más importante primero. Eso le proporcionará la mayor mejora en rendimiento, y es posible que no necesite corregir más de algunos de estos problemas para cumplir con los requisitos de rendimiento.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
5. Utilice StringBuilder para concatenar cadenas
Hay muchas opciones diferentes para concatenar cadenas en Java. Puede, por ejemplo, usar un simple + o +=, el viejo StringBuffer o StringBuilder.
Entonces, ¿Que enfoque debería preferir?
La respuesta depende del código que concatena el String. Si está agregando contenido nuevo al String en forma programática, por ejemplo en un bucle for, debería utilizar StringBuilder. Es fácil de utilizar y proporciona un mejor rendimiento que StringBuffer. Pero tenga en cuenta que StringBuilder, a diferencia de StringBuffer, no es seguro para sub procesos y puede no se adecuado para todos los casos de uso.
Solo necesita crear una instancia de un nuevo StringBuilder y llamar al método add() para agregar una nueva parte del String. Y cuando haya agregado todas las partes, puede llamar al método toString() para recuperar el String concatenado.
1 2 3 4 5 6 | StringBuilder sb = new StringBuilder(“This is a test”); for (int i=0; i<10; i++) { sb.append(i); sb.append(” “); } log.info(sb.toString()); |
En el ejemplo anterior, como puede ver, puede proporcionar el primer elemento del String al método del constructor. Eso creará un nuevo StringBuilder que contiene el String proporcionado y una capacidad para 16 caracteres adicionales. Cuando agrega más caracteres a StringBuilder, su JVM aumentará dinámicamente el tamaño del StringBuilder.
Si ya sabe cuántos caracteres contendrá su String, puede proporcionar este número a un método de constructor diferente para instanciar un StringBuilder con la capacidad definida. Eso mejora la eficiencia aún más por que no necesita ampliar dinámicamente la capacidad.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
6. Utilice + para concatenar String en una sentencia
Cuando implementó su primera aplicación en Java, alguien probablemente le dijo que no debería concatenar cadenas con +. Y eso es correcto si está concatenando cadenas en su lógica de aplicación. Las cadenas son inmutables, y el resultado de cada concatenación de cadenas se almacena en un nuevo objeto String. Eso requiere memoria adicional y ralentiza su aplicación, especialmente si está concatenando múltiples cadenas dentro de un bucle.
En estos casos, debe seguir la sugerencia número 5 y usar un StringBuilder.
Pero ese no es el caso si solo estás dividiendo un String en varias líneas para mejorar la legibilidad de tu código.
1 2 3 | Query q = em.createQuery(“SELECT a.id, a.firstName, a.lastName ” + “FROM Author a ” + “WHERE a.id = :id”); |
En estas situaciones, debe concatenar sus cadenas con un simple +. Su compilador Java optimizará esto y realizará la concatenación en tiempo de compilación. Por lo tanto, en tiempo de ejecución, su código solo usará 1 cadena, y no se requerirá concatenación.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
7. Utiliza primitivas donde sea posible
Otra manera rápida y fácil de evitar cualquier sobrecarga y mejorar el rendimiento de su aplicación es utilizar tipos primitivos en lugar de sus clases contenedoras. Por lo tanto, es mejor utilizar un int en lugar de un Integer, o un doble en lugar de un Double. Eso permite que su JVM almacene el valor en la pila en lugar del almacenamiento dinámico para reducir el consumo de memoria y, en general, manejarlo de manera más eficiente.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
8. Trate de evitar BigInteger y BigDecimal
Como ya estamos hablando de tipos de datos, también deberíamos echar un vistazo rápido a BigInteger y BigDecimal. Especialmente este último es popular debido a su precisión. Pero eso tiene un precio.
BigInteger y BigDecimal requieren mucha más memoria que un simple largo o doble y ralentizan todos los cálculos de manera espectacular. Entonces, mejor piense dos veces si necesita la precisión adicional, o si sus números excederán el rango de un largo. Esto podría ser lo único que necesita cambiar para solucionar sus problemas de rendimiento, especialmente si está implementando un algoritmo matemático.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
9. Verifique el nivel de Log actual
Esta recomendación debería ser obvia, pero desafortunadamente, puede encontrar muchos códigos que la ignoran. Antes de crear un mensaje de depuración, siempre debe verificar primero el nivel de registro actual. De lo contrario, podría crear un String con su mensaje de registro que luego se ignorará.
Aquí hay dos ejemplos de cómo NO debes hacerlo.
1 2 3 4 | // don’t do this log.debug(“User [” + userName + “] called method X with [” + i + “]”); // or this log.debug(String.format(“User [%s] called method X with [%d]”, userName, i)); |
En ambos casos, realizará todos los pasos necesarios para crear el mensaje de registro sin saber si el framework de registro usará el mensaje de registro. Es mejor verificar el nivel de registro actual primero antes de crear el mensaje de depuración.
1 2 3 4 | // do this if (log.isDebugEnabled()) { log.debug(“User [” + userName + “] called method X with [” + i + “]”); } |
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
10. Utilice Apache Commons StringUtils.Replace en lugar de String.replace
En general, el método String.replace funciona bien y es bastante eficiente, especialmente si está utilizando Java 9. Pero si su aplicación requiere muchas operaciones de reemplazo y no ha actualizado a la versión más reciente de Java, todavía tiene sentido buscar alternativas más rápidas y eficientes.
Un candidato es el método StringUtils.replace de Apache Commons Lang. Como Lukas Eder describió en una de sus publicaciones recientes en el blog, supera dramáticamente el método de reemplazo de String.s de Java 8.
Y solo requiere un cambio mínimo. Debe agregar una dependencia de Maven para el proyecto Lang de Commons Apache a su aplicación pom.xml, y reemplazar todas las llamadas del método String.replace con el método StringUtils.replace.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
11. Lleve a Caché recursos costoso, como conexión a base de datos
El almacenamiento en caché es una solución popular para evitar la ejecución repetida de fragmentos de código costosos o usados con frecuencia. La idea general es simple: reutilizar dichos recursos es más barato que crear uno nuevo una y otra vez.
Un ejemplo típico es el almacenamiento en caché de las conexiones de la base de datos en un grupo. La creación de una nueva conexión lleva tiempo, que puede evitar si reutiliza una conexión existente.
También puede encontrar otros ejemplos en el lenguaje Java en sí. El método valueOf de la clase Integer, por ejemplo, almacena en caché los valores entre -128 y 127. Se podría decir que la creación de un nuevo Integer no es demasiado cara, pero se utiliza con tanta frecuencia que proporciona el almacenamiento en caché de los valores más utilizados. un beneficio de rendimiento.
Pero cuando piense en el almacenamiento en caché, tenga en cuenta que su implementación de almacenamiento en caché también crea una sobrecarga. Necesita gastar memoria adicional para almacenar los recursos reutilizables, y es posible que necesite administrar su caché para hacer accesibles los recursos o eliminar los obsoletos.
Por lo tanto, antes de comenzar a almacenar en caché los recursos, asegúrese de utilizarlos con la frecuencia suficiente para compensar la sobrecarga de la implementación de la memoria caché.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
Resumen
Como ha visto, a veces no requiere mucho trabajo mejorar el rendimiento de su aplicación. La mayoría de las recomendaciones en esta publicación solo necesitan un pequeño esfuerzo adicional para aplicarlas a su código.
Pero como de costumbre, las recomendaciones más importantes son independientes del lenguaje:
- No optimices antes de saber que es necesario
- Utilice un Profiler para encontrar el verdadero cuello de botella
- Trabaja primero en el mayor cuello de botella
Este artículo esta basado en 11 Simple Java Performance Tuning Tips[/vc_column_text][/vc_column][/vc_row]