Корректно завершая референт SoftReference

В ответ на Kurt я предложил бы эту альтернативу его BuiltTest. МН сценарий.

use strict;
use warnings;
use Module::Build;

my $build = Module::Build->resume (
  properties => {
    config_dir => '_build',
  },
);

$build->dispatch('build');
$build->dispatch('test');

Это снова использует сборку базы данных Сборкой. МН (и таким образом предполагает, что уже работал).

8
задан mindas 30 November 2009 в 15:05
поделиться

4 ответа

Ответ Тома правильный, однако код, добавленный к вопросу, отличается от того, что было предложено Томом. То, что предлагал Том, выглядит примерно так:

class Bloat {  // just a heap filler really
    public Reader res;
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

    private final int ii;

    public Bloat(final int ii, Reader res) {
       this.ii = ii;
       this.res = res;
    }
 }

 // as recommended by Tom Hawtin
 class MySoftBloatReference extends SoftReference<Bloat> {
    public final Reader hardRef;

    MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
       super(referent, q);
       this.hardRef = referent.res;
    }
 }

 //...meanwhile, somewhere in the neighbouring galaxy...
 {
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
    int i=0;

    while(i<50000) {
        set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));

        MySoftBloatReference polled = (MySoftBloatReference) rq.poll();

        if (polled != null) {
            // close the reference that we are holding on to
            try {
                polled.hardRef.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        i++;
    }
}

Обратите внимание, что большая разница в том, что жесткая ссылка относится к объекту, который необходимо закрыть. Окружающий объект может и будет собираться мусором, поэтому вы не попадете в OOM, однако у вас все равно будет возможность закрыть ссылку. Как только вы выйдете из цикла, он также будет удален. Конечно, в реальном мире вы, вероятно, не сделаете res членом общедоступного экземпляра.

Тем не менее, если у вас есть ссылки на открытые файлы, вы очень серьезно рискуете исчерпать их из них, прежде чем у вас закончится память. Вы, вероятно, также захотите иметь кеш LRU, чтобы гарантировать, что вы храните не более пальца в воздухе 500 открытых файлов. Они также могут относиться к типу MyReference, чтобы при необходимости их можно было собирать мусором.

Чтобы немного пояснить, как работает MySoftBloatReference, базовый класс SoftReference по-прежнему содержит ссылку на объект, который забирает все памяти. Это тот объект, который вам нужно освободить, чтобы предотвратить OOM. Однако, если объект освобожден, вам все равно нужно освободить ресурсы, которые использует Bloat, то есть Bloat использует два типа ресурсов: память и дескриптор файла, оба этих ресурса должны быть освобождены, или вы запустите из одного или другого ресурса. SoftReference обрабатывает нагрузку на ресурс памяти, освобождая этот объект, однако вам также необходимо освободить другой ресурс - дескриптор файла. Поскольку раздувание уже было освобождено, мы не можем использовать его для освобождения связанного ресурса, поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано о том, что Bloat был освобожден, т.е. когда ссылка появляется в ReferenceQueue, MySoftBloatReference может также закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В этом конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

дескриптор файла. Поскольку раздувание уже было освобождено, мы не можем использовать его для освобождения связанного ресурса, поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано, что Bloat был освобожден, то есть когда ссылка появляется в ReferenceQueue, MySoftBloatReference также может закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В данном конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

дескриптор файла. Поскольку раздувание уже было освобождено, мы не можем использовать его для освобождения связанного ресурса, поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано, что Bloat был освобожден, то есть когда ссылка появляется в ReferenceQueue, MySoftBloatReference также может закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В этом конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

мы не можем использовать его для освобождения связанного ресурса, поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано о том, что Bloat был освобожден, т.е. когда ссылка появляется в ReferenceQueue, MySoftBloatReference может также закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В этом конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

мы не можем использовать его для освобождения связанного ресурса, поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано, что Bloat был освобожден, то есть когда ссылка появляется в ReferenceQueue, MySoftBloatReference также может закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В этом конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано о том, что Bloat был освобожден, т.е. когда ссылка появляется в ReferenceQueue, MySoftBloatReference может также закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В данном конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

поэтому MySoftBloatReference сохраняет жесткую ссылку на внутренний ресурс, который необходимо закрыть. После того, как было проинформировано, что Bloat был освобожден, то есть когда ссылка появляется в ReferenceQueue, MySoftBloatReference также может закрыть связанный ресурс с помощью жесткой ссылки, которая у него есть.

EDIT: Обновлен код так что он компилируется при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В данном конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

затем MySoftBloatReference может также закрыть связанный ресурс с помощью имеющейся у него жесткой ссылки.

РЕДАКТИРОВАТЬ: Обновлен код, чтобы он компилировался при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В данном конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

затем MySoftBloatReference может также закрыть связанный ресурс с помощью имеющейся у него жесткой ссылки.

РЕДАКТИРОВАТЬ: Обновлен код, чтобы он компилировался при добавлении в класс. Он использует StringReader для иллюстрации концепции закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В данном конкретном случае закрытие этого потока фактически является бездействующим, поэтому в нем нет необходимости, но показано, как это сделать, если это необходимо.

5
ответ дан 5 December 2019 в 12:10
поделиться

Для конечного числа ресурсов: Подкласс SoftReference . Мягкая ссылка должна указывать на окружающий объект. Сильная ссылка в подклассе должна ссылаться на ресурс, чтобы он всегда был надежно доступен. При чтении через ReferenceQueue опрос ресурс может быть закрыт и удален из кэша. Кэш должен быть освобожден правильно (если SoftReference сам является сборщиком мусора, его нельзя поставить в очередь в ReferenceQueue ).

Будьте осторожны, чтобы у вас было только конечное количество ресурсов, не освобожденных в кеше - удалить старые записи (действительно, вы можете отбросить мягкие ссылки с конечным кешем, если это подходит вашей ситуации). Обычно более важным является ресурс, не связанный с памятью,

7
ответ дан 5 December 2019 в 12:10
поделиться

@Paul - большое спасибо за ответ и разъяснения.

@Ran - Я думаю, что в вашем текущем коде i ++ отсутствует в конце цикла. Кроме того, вам не нужно выполнять rq.remove () в цикле, поскольку rq.poll () уже удаляет верхнюю ссылку, не так ли?

Несколько моментов:

1) Мне пришлось добавить Thread. Оператор sleep (1) после i ++ в цикле (для обоих решений Пола и Рана), чтобы избежать OOM, но это не имеет отношения к общей картине и также зависит от платформы. Моя машина оснащена четырехъядерным процессором и работает под управлением Sun Linux 1.6.0_16 JDK.

2) После изучения этих решений я думаю, что буду использовать финализаторы. В книге Блоха приводятся следующие причины:

  • нет гарантии, что финализаторы будут выполнены быстро, поэтому никогда не делайте ничего критичного по времени в финализаторе - и нет никаких гарантий для SoftRererences!
  • Никогда не полагайтесь на финализатор для обновления критического постоянного состояния - я не
  • , использование финализаторов сильно снижает производительность - - В моем наихудшем случае я бы завершал примерно один объект в минуту или около того. Думаю, я смогу с этим смириться.
  • используйте try / finally - о да, я определенно буду!

Необходимость создавать огромное количество подмостей только для того, что кажется простой задачей, мне не кажется разумным. Я имею в виду, буквально, скорость WTF в минуту будет довольно высокой для любого, кто смотрит на такой код.

3) К сожалению, нет возможности разделить точки между Полом, Томом и Раном :( Надеюсь, Том не будет возражать, так как у него их уже много :) Судить между Полом и Раном было намного сложнее - я думаю, что оба ответа работают и верны. Я устанавливаю флаг принятия только для ответа Пола, потому что он был оценен выше (и имеет более подробное объяснение), но решение Ран совсем неплохо и, вероятно, было бы моим выбором, если бы я решил реализовать его с помощью SoftReferences. Спасибо, ребята!

0
ответ дан 5 December 2019 в 12:10
поделиться

Амм.
(Насколько я знаю) Вы не можете удерживать палку за оба конца. Вы либо держитесь за свою информацию, либо отпускаете ее.
Однако ... вы можете сохранить некоторую ключевую информацию, которая позволит вам завершить работу. Конечно, ключевая информация должна быть значительно меньше «реальной информации» и не должна содержать реальной информации в графе достижимых объектов (в этом вам могут помочь слабые ссылки).
Основываясь на существующем примере (обратите внимание на поле ключевой информации):

public class Test1 {
    static class Bloat {  // just a heap filler really
        private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;

        private final int ii;

        public Bloat(final int ii) {
            this.ii = ii;
        }
    }

    // as recommended by Tom Hawtin
    static class MyReference<T, K> extends SoftReference<T> {
        private final K keyInformation;

        MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) {
            super(referent, q);
            this.keyInformation = keyInformation;
        }

        public K getKeyInformation() {
            return keyInformation;
        }
    }

    //...meanwhile, somewhere in the neighbouring galaxy...
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
        Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
        int i = 0;

        while (i < 50000) {
            set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq));

            final Reference<? extends Bloat> polled = rq.poll();

            if (polled != null) {
                if (polled instanceof MyReference) {
                    final Object keyInfo = ((MyReference) polled).getKeyInformation();
                    System.out.println("not null, got key info: " + keyInfo + ", finalizing...");
                } else {
                    System.out.println("null, can't finalize.");
                }
                rq.remove();
                System.out.println("removed reference");
            }

Изменить:
Я хочу подробнее остановиться на «либо держи свою информацию, либо отпусти». Предполагая, что вы каким-то образом храните свою информацию. Это вынудило бы сборщик мусора снять пометку с ваших данных, что привело бы к фактической очистке данных только после того, как вы закончите с ними, во втором цикле сборщика мусора. Это возможно - и именно для этого нужен finalize (). Поскольку вы заявили, что не хотите, чтобы второй цикл происходил, вы не можете хранить свою информацию (если a -> b, то! B ->! A). что означает, что вы должны отпустить это.

Edit2:
На самом деле, произойдет второй цикл - но для ваших «ключевых данных», а не для «крупных раздутых данных». Фактические данные будут очищены в первом цикле.

Edit3:
Очевидно, что реальное решение будет использовать отдельный поток для удаления из очереди ссылок (не использовать poll (), remove (), блокировку в выделенном потоке).

2
ответ дан 5 December 2019 в 12:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: