2012-02-27 9 views
19

Estoy leyendo en el ajuste de JVM, y se me ocurrió que la JVM sigue moviendo objetos cuando hace GC. Pero los objetos de Java tienen referencias entre sí, lo que uno supondría que se implementan como punteros, pero la JVM no puede repasar todo el montón después de cada movimiento de objetos, y actualizar todas las referencias; seguramente eso tomaría para siempre. Entonces, ¿cómo resuelve las referencias, si las referencias no cambian, pero la ubicación física de los objetos?Si la JVM sigue moviendo objetos cuando tiene GC, ¿cómo resuelve las referencias?

He leído mucho sobre la JVM, pero eso nunca fue explicado, ni siquiera insinuado, en ningún lado.

[EDITAR] Mi punto es que las referencias son cosas de una sola dirección. Pasar del puntero al apuntado es "instantáneo", pero al revés requerirá un escaneo de montón completo. Si bien es posible, parece poco probable. Si los objetos 10K sobreviven a una colección menor, ¿cuánto tiempo llevaría hacer un escaneo de montón completo 10K veces para actualizar las referencias a esos objetos? Debe haber algún tipo de algoritmo optimizado o estructura utilizada.

+4

Pregunta similar: http://stackoverflow.com/questions/88852/does-the-java-vm-move-objects-in-memory-and-if-so-how – sleske

Respuesta

13

Si está realmente interesado en cómo funcionan los recolectores de basura, puedo recomendar los 2 libros de Richard Jones sobre la recolección de basura. Los enlaces/referencias son here. Esto no es específicamente sobre la recolección de basura de Java.

(tengo una copia del libro mayor, y el nuevo que está en mi lista de compras.)


Aquí es una versión simple de cómo un colector copia se ocupa de este problema.

Un colector de copia funciona al copiar objetos de un espacio (el del espacio) a otro (el espacio).

Específicamente, el GC recorre el gráfico de los objetos alcanzables dentro del espacio "desde", comenzando desde cada una de las raíces de la GC. Cada vez que encuentra una referencia a un nodo (en un campo de instancia, campo estático, marco de pila, etc.), comprueba el objeto al que apunta la referencia para ver si se ha marcado como visitado.

  • Si aún no está marcada, la GC hace lo siguiente:

    1. Marca el objeto en el espacio de.
    2. Copia el objeto en el espacio.
    3. Almacena la dirección del objeto en el espacio en el objeto del espacio. (Esto es como una dirección de reenvío).
    4. Visita recursivamente cada campo de referencia de la copia de espacio del objeto.

    El resultado de esto es la referencia al objeto to-space.

  • Si el objeto ya se ha marcado, el GC busca la dirección de reenvío y la devuelve.

La ubicación (en-espacio, o alguna raíz GC) donde el GC tiene la referencia de se actualiza con el puntero al objeto en-espacio.

Si sigue todo eso, verá que el GC no necesita ir a buscar todos los lugares que contienen una referencia a un objeto movido dado. En cambio, simplemente encuentra todos los lugares en el cruce de los objetos alcanzables. Por supuesto, el GC hace tiene que hacer ese recorrido, pero hay varias técnicas para reducir la cantidad de desplazamiento que se necesita hacer en cada ciclo de GC.

Si no ha seguido lo anterior, entonces POR FAVOR, lea uno de los libros de texto que he recomendado. Harán un trabajo mucho mejor de explicarlo de lo que yo puedo hacer. También encontrará material sobre cómo otros tipos de GC tratan este tema.


Los Java HotSpot GC son todos los coleccionistas de copiado de una forma u otra.Las cosas se vuelven un poco más complicadas que mi descripción anterior para la recopilación paralela y concurrente, pero el mecanismo de "reenvío de direcciones" es común para todas ellas.

(no hay muchos trabajos publicados u otra documentación pública sobre HotSpot GC, y la mayor parte del material que existe asume que el lector tiene una buena comprensión de lo moderna obra recolectores de basura.)

+0

. Pero, ¿qué sucede si tiene objetos en la generación anterior que se refieren a objetos en la nueva generación (que se están moviendo)? Tienes que pasar por toda la generación anterior buscando referencias a todo lo que se está moviendo. Parece que sería más eficiente hacer que cada referencia pase por una capa de indirección que mantiene la ubicación real del objeto. –

+0

Eso es específico de GC. Pero el enfoque general es que la JVM ejecuta una secuencia de "barrera de escritura" cuando actualiza un campo de puntero en un objeto. La barrera de escritura es responsable de anotar el antiguo puntero de generación joven. (Por ejemplo, el recopilador G1 hace esto usando "cartas" y "conjuntos recordados"). –

4

la JVM no puede ir por todo el montón después de cada vez que movió alrededor de los objetos, y actualizar todas las referencias

No soy un experto en la GC a mí mismo, pero en lo que Lo sé, eso es más o menos lo que hace. Ver p. este texto:

Por el contrario, un colector de copia copia los objetos alcanzables a otra región de la memoria mientras se están recorriendo. [...] Después de tal un recorrido en todos los objetos que sobreviven residen en una región contigua de memoria, y todos los punteros se han actualizado para apuntar a los nuevos escenarios de objetos. [...] Durante el proceso, el GC crea un gráfico de objetos para rastrear los objetos "activos" de modo que pueda actualizar las referencias a cualquier objeto que mueva.

(http://wiki.osdev.org/Garbage_collection#Copy_collectors, énfasis mío).

En cuanto a esto "tomar para siempre": la idea principal detrás de un recolector de basura (o movimiento) es que solo una pequeña cantidad de objetos tendrá que moverse, porque la mayoría de los casos ya están muertos (es decir, la mayoría los casos son muy efímeros). Por lo tanto, el número de objetos que se mueven es pequeño y, con suerte, el número de referencias que los señalan también es bastante pequeño.

En cualquier caso, el GC debe construir una lista de referencias de objetos de todos modos (para averiguar qué objetos están aún referenciados/vivos y deben copiarse), por lo que probablemente pueda reutilizar esa lista para actualizar las referencias. Entonces, la única actualización es "trabajo extra".

+0

+1 como referencia, pero desafortunadamente no es JVM específico. Voy a comentar como una pregunta de edición ... –

+0

En realidad, el GC no abarca todo el montón en la forma en que el OP describe en su pregunta ... –

1

la JVM no puede repasar todo el montón después de cada vez que movió objetos y actualiza todas las referencias; sin duda que sería tomar para siempre

Seguro que no escanear a través de todo el montón de detectar el objeto que ya no se hace referencia por nadie y marcarlos como elegibles para ser recogidos y colocar todos los objetos activos en una memoria compacta área para evitar la fragmentación.

cómo lo hace depende de los algoritmos de recolección de basura usados ​​pero es un proceso que lleva tiempo y que de hecho es una razón por la Java (per se) no se puede utilizar en las limitaciones en tiempo real

+1

"escanear todo el montón" solo ocurre en * completo GC *, pero la posición de los objetos también cambia en GC menor, y esos objetos podrían ser apuntados por objetos en la generación anterior, que no son parte de GC menor. –

+0

Hay muchos algoritmos de recolección de basura e incluso jdk no usa el mismo en 1.4 con 1.5 o posterior. Tal vez deberías estudiar el algoritmo utilizado en la versión que te interesa para obtener la respuesta exacta que buscas – Cratylus

+0

Acabo de terminar de leer " Rendimiento de Java "(ISBN-10: 0137142528) publicado en octubre de 2011, que es LA referencia. Lamentablemente, eso no se explica (o de alguna manera me lo perdí). –

0

Por lo general, los colectores dont caminar todo el montón. Identifican objetos vivos y los atraviesan.

Por ejemplo, el recolector de copias en Hotspot, comienza con las raíces e identifica todos los objetos en vivo. Una vez que se identifican los objetos en vivo, se copian en un nuevo espacio en el montón. Al recorrer todos los objetos en vivo, realiza las modificaciones de dirección requeridas para los objetos en vivo.

Una vez hecho esto, todo lo que queda en el espacio antiguo son los objetos muertos y los objetos que ya se han movido. Este espacio libre es recuperado por GC y se usa en el futuro para mover otros objetos en vivo hacia él.

El tiempo empleado es proporcional al número de objetos activos en el montón.

2

No estoy del todo seguro de que esta sea la forma en que se gestionan las referencias de objetos en el montón, pero sospecho que las referencias de objetos que Java VM entrega a nuestros programas NO son las direcciones de memoria reales, pero las referencias JVM internas a la dirección real en JVM (HashMap o estructura similar). Es decir. todos los objetos que se refieren a objectA tendrán referencias [NOT address] a objectA, cuando ocurre GC JVM NO necesita actualizar las referencias en todos estos objetos, solo la dirección real modificada en su propio HashMap.

+0

Esto es lo que estaba asumiendo, pero la otra respuesta no parece estar de acuerdo. Lamentablemente, hasta ahora todo es solo conjeturas, ya que nadie podría señalar algún enlace de Sun/Oracle que explique esto. –

Cuestiones relacionadas