Optimizar los programas a través de las especificaciones del código Java, optimizar el uso de la memoria y evitar la fuga de memoria
Los recursos disponibles para los programas para utilizar (memoria, tiempo de CPU, ancho de banda de red, etc.) son limitados. La optimización generalmente incluye dos aspectos: reducir el tamaño del código y mejorar la eficiencia de ejecución del código. Este artículo analiza principalmente cómo mejorar la eficiencia del código.
En los programas Java, la mayoría de las razones de los problemas de rendimiento no están en el idioma Java, sino en el programa en sí. Es muy importante desarrollar buenos hábitos de escritura de código, como aplicar correctamente e hábilmente la clase java.lang.string y java.util.vector class, que puede mejorar significativamente el rendimiento del programa. Analicemos este problema en detalle a continuación.
1. Intenta especificar que el modificador final de la clase.
En la API de Java Core, hay muchos ejemplos de aplicación final, como java.lang.string. Especificar final para la clase de cadena evita que las personas sobrescriban el método Longitud (). Además, si una clase se especifica como final, todos los métodos de esa clase son finales. El compilador Java buscará oportunidades para en línea todos los métodos finales (esto está relacionado con la implementación específica del compilador). Este movimiento puede mejorar el rendimiento en un promedio del 50%.
2. Intenta reutilizar el objeto.
Especialmente cuando se usa objetos de cadena, StringBuffer se usa en su lugar cuando se produce la concatenación de cadenas. Dado que el sistema no solo toma tiempo para generar objetos, también puede tomar tiempo recolectar y procesar estos objetos en el futuro. Por lo tanto, generar demasiados objetos tendrá un gran impacto en el rendimiento del programa.
3. Intente usar variables locales.
Otras variables, como variables estáticas, variables de instancia, etc., se crean en el montón y son más lentos. Además, las variables locales pueden optimizarse aún más dependiendo del compilador específico/JVM. Consulte "Use las variables de pila siempre que sea posible".
4. No repita la inicialización de las variables <Br /> de forma predeterminada, al llamar al constructor de clases, Java inicializará la variable a un cierto valor: todos los objetos se establecen en variables nulas y enteras (byte, short, int, long) set) set) A 0, las variables flotantes y dobles se establecen en 0.0, y los valores lógicos se establecen en FALSO. Esto debe notarse especialmente cuando una clase se deriva de otra, porque cuando se crea un objeto con la nueva palabra clave, todos los constructores en la cadena de constructor se llaman automáticamente.
5. En el desarrollo del sistema de aplicación Java + Oracle, intente usar el formulario en mayúsculas en Java para reducir la carga de análisis del analizador Oracle.
6. Durante la programación de Java, tenga cuidado al realizar conexiones de base de datos y operaciones de transmisión de E/S.
Debido a que el funcionamiento de estos objetos grandes causará una sobrecarga del sistema grande, y si no tiene cuidado, conducirá a graves consecuencias.
7. Dado que el JVM tiene su propio mecanismo GC, no requiere demasiada consideración de los desarrolladores de programas, lo que reduce la carga de los desarrolladores en cierta medida, pero también pierde los peligros ocultos. En el sistema.
La condición para que JVM recicle la basura es que el objeto no se hace referencia; Por lo tanto, se recomienda que lo establezcamos manualmente en NULL después de que se usa el objeto.
8. Al usar el mecanismo de sincronización, intente usar la sincronización del método en lugar de la sincronización del bloque de código.
9. Minimice los cálculos repetidos de variables <Br /> Por ejemplo: para (int i = 0; i <list.size; i ++) {
…
}
Debe ser reemplazado por:
para (int i = 0, int len = list.size (); i <len; i ++) {
…
}
10. Intente adoptar la estrategia de carga perezosa, es decir, comenzar a crear cuando sea necesario.
Por ejemplo: String Str = "AAA";
if (i == 1) {
list.add (str);
}
Debe ser reemplazado por:
if (i == 1) {
Cadena str = "aaa";
list.add (str);
}
11. Use anormalidades con precaución
La anormalidad no es buena para el rendimiento. Para lanzar una excepción, primero debe crear un nuevo objeto. El constructor de la interfaz de lanzamiento llama al método local (nativo) llamado FillinStackTrace (), y el método FillInStackTrace () verifica la pila y recopila información de trazas de llamadas. Mientras se lance una excepción, la VM debe ajustar la pila de llamadas porque se crea un nuevo objeto durante el procesamiento. Las excepciones solo se pueden usar para el manejo de errores y no se deben usar para controlar el flujo del programa.
12. No lo use en un bucle:
Intentar {
} atrapar() {
}
Debe colocarse en la capa más externa.
13. Uso de StringBuffer:
StringBuffer representa una cadena variable de escritura.
Hay tres métodos de construcción:
StringBuffer (); // asignado 16 caracteres del espacio
Stringbuffer (tamaño int);
StringBuffer (String Str); /Asignar 16 caracteres + Str.Length () Espacio de caracteres Puede establecer su capacidad de inicialización a través del constructor StringBuffer, que puede mejorar significativamente el rendimiento.
El constructor mencionado aquí es StringBuffer (int longitud), y el parámetro de longitud indica el número de caracteres que el StringBuffer actual puede contener. También puede usar el método EnsureCapacity (int mínimo de capacidad mínima) para establecer su capacidad después de que se crea el objeto StringBuffer. Primero, echemos un vistazo al comportamiento predeterminado de StringBuffer, y luego encontremos una mejor manera de mejorar el rendimiento.
StringBuffer mantiene una matriz de caracteres internamente. Cuando el StringBuffer alcanza su capacidad máxima, aumentará su capacidad para 2 veces la capacidad actual y agregará 2, es decir (2*Valor anterior +2). Si usa el valor predeterminado, después de la inicialización, agregue caracteres. a 70 (2*34+2). No importa qué, mientras el StringBuffer alcance su capacidad máxima, tiene que crear una nueva matriz de personajes y luego volver a copiar los personajes antiguos y nuevos. Por lo tanto, no está mal establecer siempre un valor de capacidad de inicialización razonable para StringBuffer, lo que generará ganancia de rendimiento inmediato. Esto muestra el papel de ajustar el proceso de inicialización de StringBuffer. Por lo tanto, usar un valor de capacidad adecuado para inicializar un StringBuffer es siempre una sugerencia óptima.
14. Use la clase Java java.util.vector razonablemente.
En pocas palabras, un vector es una matriz de instancias Java.Lang.Object. Vector es similar a una matriz, y se puede acceder a sus elementos a través de un índice en forma de un entero. Sin embargo, después de la creación de un objeto de tipo vectorial, el tamaño del objeto puede ampliarse y reducirse de acuerdo con la adición o eliminación de elementos. Considere el siguiente ejemplo de agregar elementos a Vector:
Objeto bj = nuevo objeto ();
Vector V = nuevo vector (100000);
para (int i = 0;
I <100000;
A menos que haya una razón absolutamente suficiente para requerir que se inserten nuevos elementos frente a Vector cada vez, el código anterior es malo para el rendimiento. En el constructor predeterminado, la capacidad de almacenamiento inicial de Vector es de 10 elementos. La clase Vector es como la clase de objeto StringBuffer. El siguiente fragmento de código es pedidos de magnitud más rápido que el ejemplo anterior:
Objeto bj = nuevo objeto ();
Vector V = nuevo vector (100000);
para (int i = 0; i <100000; i ++) {v.add (obj);
La misma regla se aplica al método remove () de la clase vectorial. Dado que cada elemento en el vector no puede contener un "espacio" entre cada elemento, eliminar cualquier otro elemento, excepto que el último elemento hace que los elementos después del elemento eliminado avancen. Es decir, eliminar el último elemento de un vector es varias veces menos "sobrecarga" que eliminar el primer elemento.
Suponiendo que queremos eliminar todos los elementos del vector anterior, podemos usar este código:
para (int i = 0; i <100000; i ++)
{
v.remove (0);
}
Sin embargo, en comparación con el siguiente código, el código anterior son los pedidos de magnitud más lento:
para (int i = 0; i <100000; i ++)
{
v.remove (v.size ()-1);
}
La mejor manera de eliminar todos los elementos de un objeto V de type vector es:
v.removeAllelements ();
Supongamos que el objeto V de tipo vector contiene la cadena "hola". Considere el siguiente código, que elimina la cadena "Hello" de este vector:
Cadena s = "hola";
int i = v.indexof (s);
if (i! = -1) v.remove (s);
El código no parece nada malo, pero también es malo para el rendimiento. En este código, el método indexOf () busca v en secuencia para encontrar la cadena "hola", y el método eliminar (s) también debe buscar en el mismo orden. La versión mejorada es:
Cadena s = "hola";
int i = v.indexof (s);
if (i! = -1) v.remove (i);
En esta versión, damos directamente la posición de índice exacta del elemento que se eliminará en el método Remout (), evitando así la segunda búsqueda. Una mejor versión es:
Cadena S = "Hola";
Finalmente, veamos un fragmento de código sobre la clase Vector:
para (int i = 0; i ++; i <v.length)
Si V contiene 100,000 elementos, este fragmento de código llamará al método V.Size () 100,000 veces. Aunque el método de tamaño es un método simple, todavía requiere la sobrecarga de una llamada de método, al menos el JVM necesita configurar y borrar el entorno de pila para ello. Aquí, el código dentro del bucle for no modificará el tamaño del objeto de tipo vector V de ninguna manera, por lo que el código anterior se reescribe mejor en el siguiente formulario:
int tize = V.Size ();
Si bien este es un cambio simple, todavía gana el rendimiento. Después de todo, cada ciclo de CPU es precioso.
15. Al copiar una gran cantidad de datos, use el comando System.ArrayCopy ().
16. Refactorización del código: Mejore la legibilidad del código .
Por ejemplo:
public class Shopcart {private List Carts; ... public void add (elemento de objeto) {if (CARTS == NULL) {CARTS = new ArrayList ();} crts.add (elemento);} public void remo (Carts. Contiene (item)) {CARTS.Remove (item);}} Lista pública getCarts () {// return de solo lectura de la lista de lectura colección.unmodifiaBlelist (carros);} // Este método no se recomienda // esto . 17. Cree una instancia de una clase sin usar nuevas palabras clave
Al crear una instancia de una clase con la nueva palabra clave, todos los constructores en la cadena de constructor se llamarán automáticamente. Pero si un objeto implementa la interfaz clonable, podemos llamar a su método Clone (). El método Clone () no llama a ningún constructor de clase.
Al usar el patrón de diseño, si usa el modo de fábrica para crear un objeto, es muy simple usar el método Clone () para crear una nueva instancia de objeto. Por ejemplo, la siguiente es una implementación típica del patrón de fábrica:
Public Static Credit getNewCredit () {
devolver nuevo crédito ();
}
El código mejorado utiliza el método Clone () de la siguiente manera:
Crédito estático privado basecredit = new Credit ();
Public Static Credit getNewCredit () {
retorno (crédito) basecredit.clone ();
}
La idea anterior también es muy útil para el procesamiento de la matriz.
18. Multiplicación y división
Considere el siguiente código:
para (val = 0; val <100000; val += 5) {
alterx = val * 8;
}
Reemplazar la multiplicación con operaciones de cambio puede mejorar en gran medida el rendimiento. Aquí está el código modificado:
para (val = 0; val <100000; val += 5) {
alterx = val << 3;
}
El código modificado ya no se multiplica por 8, sino que usa el cambio izquierdo equivalente de 3 bits, con 1 bit por cambio izquierdo equivalente a multiplicar por 2. En consecuencia, el cambio correcto por operación de 1 bit es equivalente a dividir en 2. Vale la pena mencionar que, aunque la operación de cambio es rápida, puede dificultar el código de entender, por lo que es mejor agregar algunos comentarios.
19. Cierre sesiones inútiles en la página JSP.
Un malentendido común es que la sesión se crea cuando hay acceso al cliente. , use <> para cerrarla. Dado que la sesión consume recursos de memoria, si no planea usar la sesión, debe cerrarla en todos los JSP.
Para las páginas que no necesitan rastrear el estado de la sesión, el cierre de sesiones creadas automáticamente puede guardar algunos recursos. Use la siguiente directiva de la página: <%@ page session = "false"%>
20. JDBC y E/S
Si una aplicación necesita acceder a un conjunto de datos a gran escala, debe considerar usar la extracción de bloques. Por defecto, JDBC extrae 32 filas de datos cada vez. Por ejemplo, supongamos que queremos atravesar un conjunto de registros de 5000 filas, JDBC debe llamar a la base de datos 157 veces antes de que pueda extraer todos los datos. Si el tamaño del bloque se cambia a 512, el número de llamadas a la base de datos se reducirá a 10 veces.
21. Uso de Servlet y Memoria <Br /> Muchos desarrolladores ahorran una gran cantidad de información a las sesiones de usuario a voluntad. A veces, los objetos almacenados en la sesión no son reciclados por el mecanismo de recolección de basura en el tiempo. Desde una perspectiva de rendimiento, un síntoma típico es que el usuario siente que el sistema se está desacelerando periódicamente, pero no puede atribuir la razón a ningún componente específico. Si monitorea el espacio del montón del JVM, se manifiesta como fluctuaciones anormales de uso de la memoria.
Hay dos formas principales de resolver este tipo de problema de memoria. El primer método es implementar la interfaz httpsessionbindinglistener en todos los frijoles con un alcance de sesión. De esta manera, siempre que se implementa el método ValueUnBound (), los recursos utilizados por Bean se pueden liberar explícitamente.
Otra forma es invalidar la sesión lo antes posible. La mayoría de los servidores de aplicaciones tienen la opción de establecer el intervalo de invalidación de la sesión. Además, el método SetMaxInactiveInterval () de la sesión también se puede llamar programáticamente.
22. Use marcas de búfer
Algunos servidores de aplicaciones han agregado la función de marcado de búfer para JSP. Por ejemplo, el servidor WebLogic de BEA admite esta función desde la versión 6.0, y el proyecto Open Symphony también admite esta función. Las etiquetas de almacenamiento en búfer JSP pueden amortiguar tanto los fragmentos de página como la página completa. Cuando se ejecuta la página JSP, si el fragmento de destino ya está en el búfer, el código que genera el fragmento ya no necesitará ser ejecutado. El búfer a nivel de página captura solicitudes a la URL especificada y amortigua toda la página de resultados. Esta característica es extremadamente útil para cestas de compras, catálogos y páginas de inicio del portal. Para tales aplicaciones, el almacenamiento en búfer de nivel de página puede guardar los resultados de la ejecución de la página para solicitudes posteriores.
23. Elija el mecanismo de cita correcto
En un sistema de aplicación JSP típico, las piezas de encabezado y pie de página a menudo se extraen, y luego se introducen el encabezado y el pie de página según sea necesario. Actualmente, hay dos métodos principales para introducir recursos externos en las páginas JSP: incluir directivas e incluir acciones.
Incluir Directiva: por ejemplo < %@ include file = "Copyright.html" %>. Esta directiva introduce el recurso especificado en el tiempo de compilación. Antes de la compilación, la página con la Directiva incluir y el recurso especificado se fusionan en un archivo. Los recursos externos referenciados se determinan en el momento de la compilación, que es más eficiente que determinar los recursos en tiempo de ejecución.
Incluya acción: por ejemplo <jsp: incluir page = "copyright.jsp" />. Esta acción introduce el resultado generado después de ejecutar la página especificada. Como se completa en tiempo de ejecución, el control de los resultados de la salida es más flexible. Sin embargo, solo es rentable de usar la acción de incluir cuando el contenido citado se cambia con frecuencia, o cuando la página referenciada no se puede determinar antes de que aparezca la solicitud de la página principal.
24. Claro que ya no se necesitan sesiones a tiempo
Para borrar sesiones que ya no están activas, muchos servidores de aplicaciones tienen un tiempo de espera de sesión predeterminado, generalmente 30 minutos. Cuando el servidor de aplicaciones necesita guardar más sesiones, si la capacidad de memoria es insuficiente, el sistema operativo transferirá parte de los datos de memoria al disco. Almacene al disco e incluso pueda lanzar una excepción de "fuera de memoria". En sistemas a gran escala, las sesiones de serialización son caras. Cuando la sesión ya no es necesaria, el método httpsession.invalidate () debe llamarse a tiempo para borrar la sesión. El método httpsession.invalidate () generalmente se puede llamar en la página de salida de la aplicación.
25. No declare la matriz como: Final estática pública.
26. Discusión sobre la eficiencia transversal de Hashmap
A menudo hay operaciones transversales en pares de clave y valor en HashMap, y hay dos métodos: MAP <String, String []> Paramap = new
Hashmap <string, string [] (); ............ // El primer bucle set <String> appfielddefids = paramap.keyset (); ] valores = paramap.get (appfieldDefid); ......} // El segundo bucle para (entry <string, string []> entry: paramap.Entryset ()) {String appfielddefid = entry.getKey ( ); String [] valores = Entry.getValue (); .......} La primera implementación es significativamente menos eficiente que la segunda implementación.
El análisis es el siguiente conjunto <String> appfielddefids = paramap.keyset ();
El código es el siguiente:
Public set <K> keySet () {set <K> ks = keySet; return (ks! = null? ks: (keySet = new KeySet ()));} La clase privada KeySet extiende AbstractSet <K> {public Iterator <K > iterator () {return newkeyIterator ();} public int size () {size return size;} public boolean contiene (objeto o) {return contiene key (o);} public boolean Retember (Object O) {return Hashmap.This.RemoveEntryForKey (O)! = NULL;} public void clear () {hashmap.this.clear ();}}De hecho, devuelve un KeySet de clase privada, que se hereda de AbstractSet e implementa la interfaz SET.
Echemos un vistazo a la sintaxis de para/en bucles
para (Declaración: expresión)
declaración
Durante la fase de ejecución, se traduce en las siguientes fórmulas
para (iterator <E> #i = (expresión) .Iterator (); #i.hashnext ();) {
declaración = #I.Next ();
declaración
}
Por lo tanto, hashmap.keySet (). Iterator () se llama en la primera declaración para (String appfielddefid: appfielddefids)
Este método llama a NewKeyIterator ()
Iterador <K> newkeyIterator () {
devolver nuevo keyIterator ();
}
KeyIterator de clase privada extiende el Haswiterator <K> {
public K Next () {
regresar NextEntry (). GetKey ();
}
}
Entonces, en el FOR, el iterador utilizado en el segundo bucle para (Entrada <String, String []> Entrada: Paramap.EntrySet ()) se llama como sigue.
amable
La entrada de clase privada se extiende el hashiterator <map.entry <k, v >> {
Public Map.Entry <k, v> next () {
regresar NextEntry ();
}
}
En este momento, el primer bucle obtiene la clave, y el segundo bucle obtiene la eficiencia de entrada de HASHMAP es que el segundo bucle reflejado en el bucle. Use la get (clave de objeto) de HashMap para obtener valor de valor, ahora mire el método Get (Key de objeto) de HashMap
public v get (clave de objeto) {
Objeto K = Masknull (clave);
int hash = hash (k);
int i = indexfor (hash, table.length);
Entrada <k, v> e = tabla;
while (verdadero) {
if (e == null)
regresar nulo;
if (e.hash == hash && eq (k, e.key))
devolver E.Value;
E = E.Next;
}
}
De hecho, es usar el valor hash para que la entrada correspondiente nuevamente compare y obtenga el resultado.
En el segundo bucle obtiene el valor de entrada y luego tome directamente la clave y el valor, que es más eficiente que el primer bucle. De hecho, de acuerdo con el concepto de mapa, debería ser mejor usar el segundo bucle.
27. Uso de matriz (matriz) y arrylist
Array ([]): la más eficiente;
ArrayList: la capacidad puede crecer dinámicamente;
Según la eficiencia y la verificación de tipo, la matriz debe usarse tanto como sea posible.
ArrayList es una versión compleja de Array
ArrayList encapsula una matriz de tipo de objeto. Método de matriz.
Cuando ArrayList almacena un objeto, la información de tipo se descarta y todos los objetos se bloquean como objeto.
Nota: JDK5 ha agregado soporte para genéricos, y la verificación de tipo se puede realizar al usar ArrayList.
Desde este punto de vista, la diferencia entre ArrayList y Array se debe principalmente a la eficiencia del aumento de la capacidad dinámica.
28. Intenta usar hashmap y arraylist .
29. La diferencia entre StringBuffer y StringBuilder:
Java.lang.StringBuffer Secuencia de carácter mutable Safe Safe. Un búfer de cadena en forma de cadena, pero no se puede modificar.
StringBuilder. En comparación con esta clase, la clase Java.lang.StringBuilder generalmente debe preferirse porque admite las mismas operaciones, pero es más rápido porque no realiza la sincronización. Para un mejor rendimiento, la capacidad de STOWNGBUFFER o STOWNGBuilder debe especificarse tanto como sea posible. Por supuesto, si la cadena que opera no excede los 16 caracteres de longitud, no la necesitará. En el mismo caso, el uso de STOWNGBuilder solo puede lograr una mejora del rendimiento de aproximadamente 10% -15% en comparación con el uso de StringBuffer, pero asciende el riesgo de inseguro de múltiples subconjuntos. En una programación modular real, el programador responsable de un módulo determinado puede no ser capaz de determinar claramente si el módulo se colocará en un entorno múltiple, por lo tanto: a menos que pueda determinar que el cuello de botella de su sistema está en StringBuffer, y Asegúrese de que su módulo no se ejecute en modo multiproceso, de lo contrario use StringBuffer.
Otros suplementos:
1. Borrar objetos que ya no se usan en el tiempo y se establecen en NULL
2. Use palabras clave finales, estáticas y otras tanto como sea posible
3. Use objetos amortiguados tanto como sea posible
Cómo optimizar el código para hacer que el archivo fuente de Java y el archivo de clase compilado sean más pequeños
1 Intenta usar la herencia.
2 Abra las opciones de optimización del compilador Java: Javac -o Esta opción eliminará el número de línea en el archivo de clase y declarará algunos métodos de segmento pequeño privado, estático y final como llamadas de método en línea
3 Extraer el código común
4 No inicialice grandes matrices. almacenado en una matriz, primero puede poner estos datos en una cadena y luego analizar la cadena en una matriz durante el tiempo de ejecución
5. Los objetos de tipo de fecha ocuparán mucho espacio.
Tipo largo, luego converso hasta el tipo de fecha cuando se usa
6. Intente usar nombres cortos para nombres de clase, nombres de métodos y nombres de variables.
7 Definir variables del tipo final estático en la interfaz
8 Si las operaciones aritméticas se pueden usar para el movimiento izquierdo / derecho, no use * y / / operaciones.
2. No inicialice las variables dos veces
Java inicializa la variable a un valor conocido de forma predeterminada llamando a un constructor de clase único. Todos los objetos se establecen en NULL, Integers (Byte, Short, Int, Long) se establecen en 0, Float y Double se establecen en 0.0, y las variables booleanas se establecen en FALSO. Esto es especialmente importante para las clases que se extienden desde otras clases, al igual que todas las series de constructores se llaman automáticamente al crear un objeto usando una nueva palabra clave.
3. Haz que la clase sea final siempre que sea posible
Las clases marcadas finales no se pueden extender. Hay muchos ejemplos de esta tecnología en la API de Java central, como Java.Lang.String. Marcar la clase de cadena como final evita que los desarrolladores creen métodos de longitud que implementan ellos mismos.
Para decirlo más profundamente, si la clase es definitiva, todos los métodos de la clase son finales. El compilador Java puede en línea todos los métodos (esto depende de la implementación del compilador). En mis pruebas, he visto un aumento promedio en el rendimiento en un 50%.
9. La excepción se lanza donde se debe lanzar.
Pruebe {Some.Method1 (); (Method2Exception e) {// manejar excepción 2} try {some.method3 (); El código que se ha descargado es más fácil de optimizar el compilador
intente {some.method1 (); Catch (Method3Exception e) {// manejar la excepción 3}
10. Optimización de For Loop
Reemplazar…
para (int i = 0; i <collection.size (); i ++) {
...
}
con…
para (int i = 0, n = collection.size (); i <n; i ++) {
...
}
5. En el desarrollo del sistema de aplicación Java + Oracle, intente usar las declaraciones SQL integradas en forma de proceso en Java para reducir la carga de análisis del analizador de Oracle.
10. Intente adoptar la estrategia de carga perezosa, es decir, comenzar a crear cuando sea necesario.
Por ejemplo: String Str = "AAA";
if (i == 1) {
list.add (str);
}
Debe ser reemplazado por:
if (i == 1) {
Cadena str = "aaa";
list.add (str);
}
12. No lo use en un bucle:
Intentar {
} atrapar() {
}
Debe colocarse en la capa más externa
Lo anterior se trata de este artículo.
Tómese un tiempo para compartir el artículo con sus amigos o deje un comentario. ¡Te agradeceremos sinceramente por tu apoyo!