Pregunta

¡Todos!

Encontré un código extraño en LinkedBlokingqueue:

private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
}

¿Quién puede explicar por qué necesitamos una variable local H? ¿Cómo puede ayudar para GC?

¿Fue útil?

Solución

Para comprender mejor qué sucede, veamos cómo se ve la lista después de ejecutar el código. Primero considere una lista inicial:

1 -> 2 -> 3

Después h puntos a head y first a h.next:

1 -> 2 -> 3
|    |
h    first

Después h.next puntos a h y head puntos a first:

1 -> 2 -> 3
|   / \
h head first

Ahora, prácticamente sabemos que solo hay una referencia activa que apunta al primer elemento, que es por sí mismo (h.next = h), y también sabemos que el GC recopila objetos que no tienen más referencias activas, por lo que cuando termina el método, el jefe (antiguo) de la lista es recopilado de forma segura por el GC ya h Existe solo dentro del alcance del método.

Dicho esto, se señaló, y estoy de acuerdo con esto, que incluso con el método de Deleue clásico (es decir, solo hacer first apunta a head.next y head apunta a first) No hay más referencias que apunten hacia la vieja cabeza. Sin embargo, en este escenario, la vieja cabeza queda colgando en la memoria y todavía tiene su next campo apuntando hacia first, Mientras que en el código que publicó, lo único que queda es un objeto aislado que apunta a sí mismo. Esto puede estar provocando que el GC actúe más rápido.

Otros consejos

Si miras el JSR166 SRC, encontrarás el cometino ofensivo

http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/linkedblockingqueue.java?view(Ver V 1.51)

Esto muestra que la respuesta está en este informe de errores

http://bugs.sun.com/view_bug.do?bug_id=6805775

La discusión completa está en este hilo

http://thread.gmane.org/gmane.comp.java.jsr.166-concurrency/5758

La parte del "Ayudar GC" se trata de evitar que las cosas sangren en la tenencia.

Salud

Mate

Tal vez un poco tarde, pero la explicación actual es completamente insatisfactoria para mí y creo que tengo una explicación más sensata.

En primer lugar, Java GC hace algún tipo de rastreo de una raíz establecida de una forma u otra. Esto significa que si se recoge la cabeza vieja no leeremos el next Variable de todos modos: no hay razón para hacerlo. Por eso SI La cabeza se recoge en la próxima iteración que no importa.

El IF en la oración anterior es la parte importante aquí. La diferencia entre establecer junto a algo diferente no importa para recolectar la cabeza misma, pero puede marcar la diferencia para otros objetos.

Supongamos un GC generacional simple: si la cabeza está en el conjunto joven, de todos modos se recopilará en el próximo GC. Pero si está en el set anterior, solo se recopilará cuando hagamos un GC completo que rara vez ocurre.

Entonces, ¿qué sucede si la cabeza está en el set anterior y hacemos un joven GC? En este caso, el JVM supone que cada objeto en el montón antiguo todavía está vivo y agrega cada referencia de objetos antiguos a jóvenes a la raíz establecida para el joven GC. Y eso es exactamente lo que la tarea evita aquí: escribir en el montón antiguo generalmente está protegido con una barrera de escritura o algo para que el JVM pueda captar tales tareas y manejarlas correctamente; en nuestro caso, elimina el objeto next señalado desde el conjunto de raíces que tiene consecuencias.

Ejemplo corto:

Supongo que tenemos 1 (old) -> 2 (young) -> 3 (xx). Si eliminamos 1 y 2 ahora de nuestra lista, podemos esperar que ambos elementos sean recopilados por el próximo GC. Pero si solo ocurre un GC joven y no hemos eliminado el next Puntero en viejo, ambos elementos 1 y 2 no serán recolectados. Al contrario de esto, si hemos eliminado el puntero en 1, 2 serán recolectados por el joven GC.

Aquí hay una muestra de código que ilustra la pregunta: http://pastebin.com/ztslpupq. Realizando un GC después runWith() Y tomar un basurero para ambas versiones dice que solo hay una instancia de elemento.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top