Desde el trabajo, se ha escrito cada vez más código, el programa se ha inflado cada vez más, y la eficiencia se ha vuelto cada vez menos. Esto no está absolutamente permitido para un programador como yo que sigue la perfección. Por lo tanto, además de optimizar constantemente la estructura del programa, la optimización de la memoria y el ajuste del rendimiento se han convertido en mis "trucos" habituales.
Para optimizar y ajustar la memoria y el rendimiento de los programas de Java, definitivamente no es posible no entender los principios internos de las máquinas virtuales (o especificaciones más rigurosas). Aquí hay un buen libro "Java Virtual Machine (Segunda edición)" (de hecho) "(de hecho, este artículo es la comprensión personal del autor de Java Virtual Machines después de leer este libro). Por supuesto, los beneficios de comprender las máquinas virtuales Java no se limitan a los dos beneficios anteriores. Desde una perspectiva técnica más profunda, comprender las especificaciones e implementación de máquinas virtuales Java será más útil para que escribamos un código Java eficiente y estable. Por ejemplo, si entendemos el modelo de memoria de la máquina virtual Java y el mecanismo de reciclaje de memoria de la máquina virtual, no confiaremos demasiado en ella, pero explícitamente "liberaremos la memoria" cuando sea necesario (el código de Java no puede liberar explícitamente la memoria, pero podemos informar al recopilación de basura que el objeto debe ser reciclado reciclado al liberar la referencia del objeto), así que para reducir la memoria nocebrada; Si entendemos cómo funciona la pila Java, podemos reducir el riesgo de desbordamiento de la pila al reducir el número de capas recursivas y el número de bucles. Para los desarrolladores de aplicaciones, es posible que no involucren directamente el trabajo de la implementación subyacente de estas máquinas virtuales Java, pero comprender este conocimiento de fondo tendrá más o menos un impacto sutil y buen en los programas que escribimos.
Este artículo explicará brevemente el modelo de arquitectura y memoria de la máquina virtual Java. Si hay palabras inapropiadas o explicaciones inexactas, asegúrese de corregirlas. ¡Estoy muy honrado!
Arquitectura de máquina virtual de Java
Subsistema de carga de clase
Hay dos cargadores de clase para máquinas virtuales Java, a saber, el cargador de clase de inicio y el cargador definido por el usuario.
El subsistema de carga de clase carga la clase en el área de datos de tiempo de ejecución a través del nombre totalmente calificado de la clase (nombre del paquete y nombre de clase, el soporte de red también incluye URL). Para cada tipo que se carga, la máquina virtual Java crea una instancia de la clase Java.Lang.Class para representar el tipo, que se coloca en el área de montón en la memoria, y la información de tipo cargada se encuentra en el área de método, que es lo mismo que todos los demás objetos.
Antes de cargar un tipo, el subsistema de carga de clase no solo debe localizar e importar el archivo de clase binaria correspondiente, sino también verificar la corrección de la clase importada, asignar e inicializar la memoria para las variables de clase, y analizar las referencias del símbolo como referencias directas. Estas acciones están estrictamente en el siguiente orden:
1) Carga: busque y cargue datos binarios de tipo;
2) Conexión: realizar verificación, preparación y análisis (opcional)
3) Verifique para garantizar la corrección del tipo importado
4) Prepárese para asignar memoria para las variables de clase e inicializarlas a los valores predeterminados
5) Analice la referencia simbólica en el tipo de aplicación directa
Área de método
Para cada tipo cargado por el subsistema de carga de clase, la máquina virtual guarda los siguientes datos en el área de método:
1. Nombre de tipo totalmente calificado
2. Nombre totalmente calificado del tipo SuperClass (java.lang.object no tiene superclase)
3. Es el tipo de clase Tipo A o un tipo de interfaz
4. Escriba el modificador de acceso
5. Lista de nombre totalmente calificado ordenado de cualquier hiperinterface directa
Además de la información de tipo básico anterior, la siguiente información también se guardará:
6. Escriba la piscina constante
7. Información de campo (incluido el nombre de campo, tipo de campo, modificador de campo)
8. Información del método (incluido el nombre del método, el tipo de retorno, el número y el tipo de parámetros, los modificadores del método. Si el método no es abstracto y local, el método bytecode, la pila de operando y la tabla de tamaño y excepción del área variable local en el marco de la pila de métodos también se guardará)
9. Todas las variables de clase excepto las constantes (en realidad, son variables estáticas de la clase. Debido a que las variables estáticas son compartidas por todas las instancias y están directamente relacionadas con el tipo, son variables a nivel de clase y se guardan en el área de método como miembros de la clase)
10. Una referencia al cargador de clases
// El returado es el string de referencia de ClassLoader.class.getClassLoader () que se guardó en este momento; una referencia a la clase de clase // devolverá la cadena de referencia. Class de la clase de clase que acaba de guardar ahora ahora;
Tenga en cuenta que el área del método también puede ser reciclada por el recolector de basura.
montón
Todas las instancias de clase o matrices creadas por los programas Java en tiempo de ejecución se colocan en el mismo montón, y cada máquina virtual Java también tiene un espacio de montón, y todos los subprocesos comparten un montón (es por eso que un programa Java de múltiples subconjuntos causará problemas de sincronización en el acceso de objetos).
Dado que cada máquina virtual Java tiene diferentes implementaciones de la especificación de la máquina virtual, es posible que no sepamos qué forma cada máquina virtual Java representa instancias de objetos en el montón, pero podemos ver a las siguientes implementaciones posibles posibles:
Mostrador de programas
Para ejecutar programas Java, cada hilo tiene su propio registro de PC (contador de programas), que se crea cuando el hilo comienza, con un tamaño de una palabra, y se utiliza para guardar la ubicación de la siguiente línea de código que debe ejecutarse.
Pila de java
Cada hilo tiene una pila Java, que guarda el estado en funcionamiento del hilo en unidades de marcos de pila. Hay dos tipos de operaciones de máquinas virtuales en la pila Java: prensado y apilamiento de la pila, los cuales tienen marcos. El marco de la pila guarda datos como parámetros entrantes, variables locales, resultados de operación intermedia, etc., que aparecen cuando el método se completa y luego se libera.
Eche un vistazo a la instantánea de memoria de la marco de la pila cuando se agregan dos variables locales juntas
Pila de métodos locales
Aquí es donde Java llama a la biblioteca local del sistema operativo, utilizado para implementar JNI (interfaz nativa de Java, interfaz local Java)
Motor de ejecución
El núcleo de la máquina virtual Java controla la carga de Bytecode y análisis de Java; Para ejecutar programas Java, cada hilo es una instancia de un motor de ejecución de máquina virtual independiente. Desde el principio hasta el final del ciclo de vida del hilo, está ejecutando bytecode o ejecutando métodos locales.
Interfaz local
Conectado a la pila de métodos locales y la biblioteca del sistema operativo.
Nota: Todos los lugares mencionados en el artículo se refieren a "Especificaciones de la máquina virtual Java para las plataformas Javaee y Javase".
Práctica de optimización de memoria de la máquina virtual
Desde que se menciona la memoria, se deben mencionar las filtraciones de memoria. Como todos sabemos, Java se desarrolló a partir de la base de C ++, y un gran problema con los programas C ++ es que las filtraciones de memoria son difíciles de resolver. Aunque JVM de Java tiene su propio mecanismo de recolección de basura para reciclar la memoria, en muchos casos, los desarrolladores del programa Java no necesitan preocuparse demasiado, pero también hay problemas de fuga, que son un poco más pequeños que C ++. Por ejemplo, hay un objeto referenciado pero inútil en el programa: si el programa hace referencia al objeto, pero no lo usará o no puede usarlo en el futuro, entonces se desperdicia el espacio de memoria que toma.
Primero veamos cómo funciona GC: monitoree el estado de ejecución de cada objeto, incluida la aplicación, cita, cita, asignación, etc. Cuando el objeto ya no se cita, libere el objeto (el enfoque de GC este artículo no se explicará demasiado). Muchos programadores de Java confían demasiado en GC, pero la clave del problema es que no importa cuán bueno sea el mecanismo de recolección de basura del JVM, la memoria siempre es un recurso limitado. Por lo tanto, incluso si GC completará la mayor parte de la recolección de basura para nosotros, todavía es necesario prestar atención a la optimización de la memoria durante el proceso de codificación adecuadamente. Esto puede reducir efectivamente el número de GC, al tiempo que mejora la utilización de la memoria y maximiza la eficiencia del programa.
En general, la optimización de la memoria de las máquinas virtuales Java debe comenzar desde dos aspectos: máquinas virtuales Java y aplicaciones Java. El primero se refiere a controlar el tamaño de la partición de memoria lógica de la máquina virtual a través de los parámetros de la máquina virtual de acuerdo con el diseño de la aplicación para que la memoria de la máquina virtual complementa los requisitos de memoria del programa; Este último se refiere a la optimización de los algoritmos del programa, reduciendo la carga de GC y mejorando la tasa de éxito del reciclaje de GC.
Los parámetros para optimizar la memoria de la máquina virtual a través de los parámetros son los siguientes:
Xms
Tamaño del montón inicial
Xmx
valor máximo del montón java
1mn
Tamaño del montón de la generación joven
XSS
Tamaño de pila para cada hilo
Los anteriores son tres parámetros más utilizados, algunos:
Xx: MinheapFreeratio = 40
Porcentaje mínimo de montón libre después de GC para evitar la expansión.
Xx: maxheapFreeratio = 70
Porcentaje máximo de montón libre después de GC para evitar encogerse.
Xx: newratio = 2
Relación de tamaños de generación nuevo/antiguo. [SPARC -Cliente: 8; x86 -Server: 8; x86 -client: 12.] -Cliente: 8 (1.3.1+), x86: 12]
Xx: newsize = 2.125m
Tamaño predeterminado de la nueva generación (en bytes) [5.0 y más recientes: las máquinas virtuales de 64 bits se escalan 30% más grandes; x86: 1m; X86, 5.0 y más: 640k]
Xx: maxNewsize =
Tamaño máximo de la nueva generación (en bytes). Desde 1.4, MaxNeWSize se calcula en función de NewRatio.
XX: SurvivorRatio = 25
Relación del tamaño del espacio Eden/Survivor [SPARC en 1.3.1: 25; Otras plataformas de Solaris en 5.0 y antes: 32]
XX: PermSize =
Tamaño inicial de la generación permanente
Xx: maxPermsize = 64m
Tamaño de la generación permanente. [5.0 y más nuevo: las máquinas virtuales de 64 bits se escalan 30% más grandes; 1.4 amd64: 96m; 1.3.1 -Client: 32m.]
Lo que se menciona a continuación para mejorar la utilización de la memoria y reducir los riesgos de memoria al optimizar los algoritmos del programa es completamente empírico y es solo para referencia. Si hay alguna inapropiación, por favor corríjeme, ¡gracias!
1. Libere la referencia de objetos inútiles lo antes posible (xx = nulo;)
Mira un código:
Lista pública <Pagedata> parse (página htmlpage) {list <pagedata> list = null; Pruebe {List Valuelist = Page.getByXPath (config.getContentXPath ()); if (valuelist == null || valuelist.isEmpty ()) {Lista de retorno; } // Cree un objeto cuando sea necesario, guarde la memoria y mejore la lista de eficiencia = new ArrayList <Pagedata> (); Pagedata Pagedata = new Pagedata (); Valor de StringBuilder = new StringBuilder (); para (int i = 0; i <valuelist.size (); i ++) {htmlelement content = (htmlelement) valuelist.get (i); Domnodelist <htmlelement> imgs = content.getElementsBytagName ("img"); if (imgs! = null &&! imgs.isempty ()) {for (htmlelement img: imgs) {try {htmlimage image = (htmlimage) img; String ruta = image.getSrCattribute (); Formato de cadena = Path.Substring (Path.lastIndexof ("."), Path.Length ()); Cadena localPath = "d:/images/" + md5helper.md5 (ruta) .replace ("//", ","). Reemplazar ("/", ",") + format; Archivo localfile = nuevo archivo (localpath); if (! localfile.exists ()) {localfile.createenewfile (); Image.Saveas (localfile); } image.setAttribute ("src", "archivo: ////" + localpath); localfile = nulo; imagen = nulo; img = nulo; } Catch (Exception e) {}} // Este objeto no se usará en el futuro. Borrar la referencia es equivalente a decirle a GC de antemano. El objeto puede reciclar imgs = nulo; } String text = content.asxml (); value.append (texto) .Append ("<br/>"); valuelista = nulo; contenido = nulo; texto = nulo; } PageData.SetContent (value.ToString ()); Pagedata.setcharset (page.getPageEncoding ()); list.add (PageData); // el Pagedata = nulo; es inútil porque la lista todavía contiene la referencia al objeto, y GC no lo reciclará valor = nulo; // no hay lista = nulo aquí; Debido a que la lista es el valor de retorno del método, de lo contrario, el valor de retorno que obtiene del método siempre estará vacío, y este tipo de error no es fácil de descubrir o excluirse} Catch (Excepción e) {} Lista de retorno; }2. Use cuidadosamente los tipos de datos de recopilación, como matrices, árboles, gráficos, listas vinculadas y otras estructuras de datos. Estas estructuras de datos son más complicadas de reciclar para GC.
3. Evite aplicar explícitamente el espacio de matriz. Cuando tenga que aplicar explícitamente, intente estimar su valor razonable con la mayor precisión posible.
4. Trate de evitar crear e inicializar una gran cantidad de objetos en el constructor predeterminado de la clase, y evitar el desperdicio innecesario de recursos de memoria al llamar a su propio constructor de la clase.
5. Trate de evitar el sistema forzado para reciclar la memoria de basura y aumentar el tiempo final del reciclaje de basura en el sistema
6. Intente usar variables de valor instantáneo al desarrollar aplicaciones de llamadas de método remoto, a menos que la persona que llame remota necesite obtener el valor de la variable de valor instantáneo.
7. Intente usar la tecnología de agrupación de objetos en escenarios apropiados para mejorar el rendimiento del sistema