2010-04-12 12 views
20

menudo he leído que en el Sun JVM objetos de corta duración ("relativamente nuevos objetos") puede ser recogido más eficiente que los objetos de larga vida de basura ("objetos relativamente viejos")¿Por qué hacer objetos pequeños y de larga duración una diferencia en la recolección de basura?

  • ¿Por qué es que ¿asi que?
  • ¿Es específico de Sun JVM o es el resultado de un principio general de recolección de basura?
+2

Me parece que muchas de las respuestas son _describiendo_ recolección de basura en lugar de la razón _por qué_ una colección de basura eden es más rápida que una colección de basura de espacio superviviente, a menos que estén implicando que es la copia de referencias a los grupos de más largo plazo eso toma el tiempo. –

Respuesta

21

más aplicaciones Java crear objetos Java y luego desecharlos con bastante rapidez por ejemplo. creas algunos objetos en un método y luego, una vez que salgas del método, todo el objeto muere. La mayoría de las aplicaciones se comportan de esta manera y la mayoría de las personas tiende a codificar sus aplicaciones de esta manera. El montón de Java se divide aproximadamente en 3 partes, generación permanente, antigua (larga vida) y generación joven (de vida corta). Young gen se divide en S1, S2 y eden. Estos son solo montones.

La mayoría de los objetos se crean en la generación joven. La idea aquí es que, dado que la tasa de mortalidad de los objetos es alta, los creamos rápidamente, los usamos y luego los descartamos. La velocidad es de esencia. A medida que crea objetos, el gen joven se llena, hasta que se produce un GC menor. En un GC menor, todos los objetos que están vivos se copian de eden y dicen S2 a S1. Luego, el 'puntero' se basa en eden y S2.

Cada copia envejece el objeto. Por defecto, si un objeto sobrevive 32 copias, viz. 32 GC menor, luego el CG calcula que va a durar mucho más tiempo. Entonces, lo que hace es mantenerlo, moviéndolo a la generación anterior. La vieja generación es solo un gran espacio. Cuando el gen viejo se llena, un GC completo o GC mayor ocurre en la vieja generación. Como no hay otro espacio para copiar, el GC debe compactarse. Esto es mucho más lento que GC menor, es por eso que evitamos hacer eso con más frecuencia.

Se puede sintonizar con el parámetro tenuring

java -XX:MaxTenuringThreshold=16 

si usted sabe que usted tiene un montón de objetos de larga vida. Puede imprimir el intervalo de varias edades de su aplicación con

java -XX:-PrintTenuringDistribution 
+0

¿Cuál es el punto de esta división de montón en partes? ¿Significa GC menor que GC está atravesando solo gráficos para objetos que están en generación joven en lugar de en toda la pila? – Malachiasz

+1

GC menor solo ocurre en gen jóvenes. Ver http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html para obtener una explicación detallada sobre los espacios generacionales. –

1

Esto se basa en la observación de que la esperanza de vida de un objeto aumenta a medida que envejece. Por lo tanto, tiene sentido mover objetos a un grupo menos frecuente una vez que alcanzan cierta edad.

Esto no es una propiedad fundamental de la forma en que los programas utilizan memoria. Podrías escribir un programa patológico que mantuviera todos los objetos por mucho tiempo (y el mismo tiempo para todos los objetos), pero esto no suele ocurrir por accidente.

+1

La mayor parte del código que produzco podría llamarse patológico. Algo de eso es francamente sociópata :-) – paxdiablo

+0

"Se podría escribir un programa patológico ... pero esto no suele suceder por accidente". Creo que muchos programas son patológicos para GC generacionales. Colas, tablas hash y cachés son todos. –

+0

@JonHarrop: las colas están sesgadas hacia la brevedad, las tablas hash no tienen voz en cuanto a cuánto tiempo permanecen sus contenidos, y la longevidad del objeto caché está ligada a la popularidad, que normalmente está muy sesgada. Podría obligar a estas estructuras de datos a comportarse patológicamente, pero no son intrínsecamente así. –

4

Esta es la colección de basura generacional . Se usa bastante ampliamente estos días. Vea más aquí: (wiki).

Esencialmente, el GC asume que es más probable que los objetos nuevos sean inalcanzables que los anteriores.

+1

Esta respuesta carece de información significativa para responder la pregunta. – stackoverflowuser2010

+0

Gracias por su opinión sobre esta respuesta de hace seis años, en la que proporcioné un nombre formal para el concepto nebuloso que la persona estaba buscando y lo relacioné con una referencia muy detallada que describe exactamente lo que estaban buscando. –

1

La JVM (por lo general) utiliza un recolector de basura generacional. Este tipo de recopilador separa la memoria del montón en varias agrupaciones, según la antigüedad de los objetos allí. El razonamiento aquí se basa en la observación de que la mayoría de los objetos son efímeros, de modo que si haces una recolección de basura en un área de memoria con objetos "jóvenes", puedes reclamar relativamente más memoria que si recoges basura " objetos.

En el punto de acceso JVM, los objetos nuevos se asignan en la llamada área de Eden. Cuando esta área se llena, la JVM barrerá el área de Eden (que no toma demasiado tiempo, porque no es tan grande). Los objetos que todavía están vivos se mueven al área de Supervivientes, y el resto se descarta, liberando a Eden para la próxima generación. Cuando la colección Eden no es suficiente, el recolector de basura pasa a las generaciones anteriores (lo que requiere más trabajo).

+0

¿Qué medios utiliza la JVM de zona activa para detectar a qué objetos más nuevos se hace referencia en los más antiguos? ¿Utiliza vallas de escritura para etiquetar objetos más antiguos que se han modificado desde la última vez que barrieron Eden, o hacer otra cosa? Según mi comprensión del GC .net, se basa en el hecho de que un objeto que no ha sido modificado desde la última colección gen-0 no puede contener ninguna referencia * directa o indirecta * a los objetos gen-0, y un objeto que no tiene ha sido modificado ya que la última colección gen-1 tampoco puede contener ninguna referencia * directa o indirecta * a los objetos gen0 o gen1. – supercat

4

Existe este fenómeno de que "la mayoría de los objetos mueren jóvenes". Muchos objetos se crean dentro de un método y nunca se almacenan en un campo. Por lo tanto, tan pronto como el método sale de estos objetos "mueren" y, por lo tanto, son candidatos para la recolección en el próximo ciclo de recolección.

Aquí se muestra un ejemplo:

public String concatenate(int[] arr) { 
    StringBuilder sb = new StringBuilder(); 
    for(int i = 0; i < arr.length; ++i) 
    sb.append(i > 0 ? "," : "").append(arr[i]); 
    return sb.toString(); 
} 

El objeto sb se convertirá en la basura tan pronto como el método retorna.

Al dividir el espacio de objetos en dos (o más) zonas basadas en la edad de la GC puede ser más eficiente: en lugar de escanear con frecuencia todo el montón, el GC frecuencia escanea sólo el vivero (el área de objetos pequeños) - que, obviamente, toma mucho menos tiempo que un escaneo de montón completo. El área de objetos más antiguos se escanea con menos frecuencia.

1

Todos los GC se comportan de esa manera. La idea básica es que intentes reducir la cantidad de objetos que debes verificar cada vez que ejecutas el GC porque es una operación bastante costosa. Entonces, si tiene millones de objetos pero solo necesita verificar algunos, es mucho mejor que tener que verificarlos todos. Además, una función de GC juega en sus manos: los objetos temporales (que ya nadie puede alcanzar) no tienen costo durante la ejecución del GC (bueno, ignoremos el método finalize() por el momento). Solo los objetos que sobreviven cuestan tiempo de CPU. Luego, se observa que muchos objetos son de corta duración.

Por lo tanto, los objetos son creados en un espacio pequeño (llamado "Edén" o "joven generación"). Después de un tiempo, todos los objetos que pueden alcanzarse se copian (= costosos) fuera de este espacio y el espacio se declara vacío (por lo que Java se olvida de todos los objetos inalcanzables, por lo que no tienen un costo ya que no tiene que ser copiado). Con el tiempo, los objetos de larga vida se mueven a espacios "antiguos" y los espacios más viejos se barren con menos frecuencia para reducir la sobrecarga del GC (por ejemplo, cada N se ejecuta, el GC ejecutará un espacio antiguo en lugar del espacio de eden).

Solo para comparar: si asigna un objeto en C/C++, necesita llamar al free() más el destructor para cada uno de ellos. Esta es una razón por la que GC es más rápido que la gestión de memoria manual tradicional.

Por supuesto, este es un aspecto bastante simplificado. Hoy en día, trabajar en GC está en el nivel del diseño del compilador (es decir, realizado por muy pocas personas). Los GC extraen todo tipo de trucos para que todo el proceso sea eficiente e imperceptible. Vea el Wikipedia article para algunos punteros.

2

Los objetos jóvenes se gestionan de manera más eficiente (no solo se recopilan, los accesos a objetos jóvenes también son más rápidos) porque se asignan en un área especial (la "generación joven"). Esa área especial es más eficiente porque se recopila "de una vez" (con todos los hilos detenidos) y ni el recopilador ni el código aplicativo tienen que tratar con el acceso concurrente de la otra.

El trade-off, aquí, es que el "mundo" se detiene cuando se recoge el "área eficiente". Esto puede inducir una pausa notable. La JVM mantiene los tiempos de pausa bajos al mantener el área eficiente lo suficientemente pequeña. En otras palabras, si hay un área administrada eficientemente, esa área debe ser pequeña.

Una heurística muy común, aplicable a muchos programas y lenguajes de programación, es que muchos objetos son muy efímeros, y la mayoría de los accesos de escritura ocurren en objetos jóvenes (los que se crearon recientemente). Es posible escribir código de aplicación que no funciona de esa manera, pero esta heurística será "mayormente cierta" en "la mayoría de las aplicaciones". Por lo tanto, tiene sentido almacenar objetos jóvenes en el área administrada eficientemente. Que es lo que hace JVM GC, y que es por lo que esa área eficiente se llama la "generación joven".

Tenga en cuenta que hay sistemas en los que toda la memoria se maneja de manera "eficiente". Cuando el GC debe ejecutarse, la aplicación se "congela" por unos segundos. Esto es inofensivo para los cálculos a largo plazo, pero perjudicial para la interactividad, por lo que la mayoría de los entornos de programación habilitados para GC modernos usan GC generacional con una generación joven de tamaño limitado.

+0

"El intercambio, aquí, es que el" mundo "se detiene cuando se recoge el" área eficiente ". Normalmente no detendría al mundo para reunir a la generación joven. –

+0

"por lo que la mayoría de los entornos de programación habilitados para GC modernos utilizan GC generacional con una generación joven de tamaño limitado". Generational GC realmente no ayuda mucho a la latencia. El rendimiento fue la verdadera motivación para elegir el GC generacional, pero ahora los retadores de la región de marca lo desafían. –

8

(consulte las explicaciones anteriores para un GC más general .. esto responde POR QUÉ nuevo es más barato que el GC anterior).

La razón por la que eden se puede borrar más rápido es simple: el algoritmo es proporcional al número de objetos que sobrevivirán GC en el espacio eden, no proporcional a la cantidad de objetos activos en todo el montón. es decir: si tiene una tasa de muerte promedio del objeto del 99% en eden (es decir, el 99% de los objetos no sobreviven al GC, que no es anormal), solo necesita mirar y copiar ese 1%. Para GC "antiguo", todos los objetos en vivo en el montón completo deben marcarse/barrirse. Eso es significativamente más caro.

+0

La mejor respuesta hasta ahora. Un objeto sin referencia se recopilará automáticamente. –

Cuestiones relacionadas