Странный код в java.util.concurrent.LinkedBlockingQueue - PullRequest
11 голосов
/ 11 января 2012

All!

Я нашел странный код в LinkedBlockingQueue:

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;
}

Кто может объяснить, зачем нам нужна локальная переменная h? Как это может помочь ГХ?

Ответы [ 4 ]

6 голосов
/ 15 января 2012

Если вы посмотрите на jsr166 src, вы найдете нарушающий коммит

http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/LinkedBlockingQueue.java?view=log (см. V 1.51)

Это показывает, что ответ находится в этом сообщении об ошибке

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

Полное обсуждение находится в этой теме

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

Бит "GC помощи" предназначен для предотвращения попадания вещей в штатную.

Приветствия

Мэтт

4 голосов
/ 14 января 2012

Может быть, немного поздно, но текущее объяснение совершенно неудовлетворительно для меня, и я думаю, что у меня есть более разумное объяснение.

Прежде всего каждый java GC выполняет какую-то трассировку из корневого наборатак или иначе.Это означает, что если старая голова будет собрана, мы все равно не будем читать переменную next - нет причин делать это.Следовательно, IF head собирается на следующей итерации, это не имеет значения.

IF в приведенном выше предложении является важной частью здесь.Разница между установкой рядом с чем-то другим не имеет значения для сбора самой головы, но может иметь значение для других объектов.

Давайте предположим, что простой GC поколения: если голова находится в молодом наборе, этовсе равно собрали в следующем ГК.Но если он в старом наборе, он будет собран только тогда, когда мы сделаем полный сбор данных, что случается редко.

Так что же произойдет, если голова будет в старом сете, и мы проведем молодую сборную?В этом случае JVM предполагает, что каждый объект в старой куче все еще жив, и добавляет все ссылки от старых до молодых объектов в корневой набор для молодого GC.И это именно то, чего здесь избегает назначение: запись в старую кучу обычно защищена барьером записи или чем-то таким, что JVM может перехватывать такие назначения и обрабатывать их корректно - в нашем случае это удаляет объект next, указанный накорневой набор, который имеет последствия.

Краткий пример:

Предположим, у нас есть 1 (old) -> 2 (young) -> 3 (xx).Если мы удалим 1 и 2 сейчас из нашего списка, мы можем ожидать, что оба элемента будут собраны следующим GC.Но если возникает только молодой GC, и мы НЕ удалили указатель next в старых, оба элемента 1 и 2 не будут собраны.В противоположность этому, если мы удалили указатель в 1, 2 будет собираться молодым ГК ..

0 голосов
/ 11 января 2012

Вот пример кода, который иллюстрирует вопрос: http://pastebin.com/zTsLpUpq. Выполнение GC после runWith() и взятие дампа кучи для обеих версий говорит, что есть только один экземпляр Item.

0 голосов
/ 11 января 2012

Чтобы лучше понять, что происходит, давайте посмотрим, как выглядит список после выполнения кода. Сначала рассмотрим начальный список:

1 -> 2 -> 3

Затем h указывает на head и first на h.next:

1 -> 2 -> 3
|    |
h    first

Тогда h.next указывает на h и head указывает на first:

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

Теперь практически мы знаем, что существует только активная ссылка, указывающая на первый элемент, который сам по себе (h.next = h), и мы также знаем, что сборщик мусора собирает объекты, у которых больше нет активных ссылок, поэтому, когда метод В завершение (старая) глава списка может быть безопасно собрана GC, поскольку h существует только в рамках метода.

Сказав это, было указано, и я согласен с этим, что даже при классическом методе удаления очереди (то есть, просто first указывают на head.next и head указывают на first), нет больше ссылок, указывающих на старую голову. Однако в этом сценарии старая голова остается в памяти и все еще имеет поле next, указывающее на first, тогда как в опубликованном вами коде остается только изолированный объект, указывающий на себя. Это может заставить GC действовать быстрее.

...