Как найти, если объект ссылается на другой во время выполнения - PullRequest
0 голосов
/ 21 сентября 2018

можно ли во время выполнения проверить, имеет ли объект прямую или косвенную ссылку на другой объект?

(я знаю, что могу использовать VisualVm или аналогичный для анализа HeapDump, но я хотел бы автоматизироватьво время выполнения)

Я работаю с WeakHashMap s, делаю что-то вроде этого:

public class MyClass {

    // the Runnable will be eventually removed if the key is collected by the GC
    private static final WeakHashMap<Object, Runnable> map = new WeakHashMap<>();

    public static void main(String[] args) {
        MyClass a = new MyClass(2);
        MyClass b = new MyClass(20);

        a = null;// no more Strong references to a
        b = null;// no more Strong references to b

        System.gc();

        for (Runnable r : map.values()) {
            r.run();
        }
        // will print (20), becouse using format() in the lambda cause a Strong
        // reference to MyClass (b) and thus the WeakHashMap will never remove b
    }

    public MyClass(int i) {

        if (i < 10) {
            map.put(this, () -> {
                System.out.println(i);
            });
        } else {
            map.put(this, () -> {
                // this is subtle, but calling format() create a strong reference 
                // between the Runnable and MyClass
                System.out.println(format(i));
            });
        }
    }

    private String format(Integer i) {
        return "(" + i + ")";
    }

}

в коде два экземпляра MyClass добавят себя(как ключ) и запускаемый (как значение) для WeakHashMap.В первом случае (a) Runnable просто вызывает System.out.println(), и когда на экземпляр a больше нет ссылок (a = null), запись будет удалена с карты.Во втором экземпляре (b) Runnable также вызывает format() функцию экземпляра MyClass.Это создаст строгую ссылку на b, и добавление Runnable на карту приведет к условию блокировки, где значение является косвенной сильной ссылкой на ключ, предотвращающий сбор сборщиком мусора.

Теперь яЗнайте, чтобы избежать этих условий (например, используя Weakreference внутри лямбды), но это очень легко пропустить в реальном сценарии и приведет к утечке памяти.

Итак, перед добавлением пары вкарту, которую я хотел бы проверить, если значение каким-то образом ссылается на ключ, и, если так, вывести исключениеЭто было бы «отладочной» задачей и будет отключено в рабочей среде, поэтому мне все равно, будет ли она медленной или взломанной.

--- update ---

Iпытаюсь разобраться с WeakListeners и избежать их немедленного сбора, если на них не ссылаются.Поэтому я регистрирую их как notifier.addWeakListener(holder, e -> { ... });, и это добавит слушателя в WeakHashMap, предотвращая его сбор до тех пор, пока держатель не будет активен.Но любая ссылка на держатель в слушателе создаст блокировку :(

Есть ли лучший способ?

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

Уже есть встроенная функция для ассоциаций, которые автоматически очищаются, обычные поля экземпляров.Т.е.

public class MyClass {

    public static void main(String[] args) {
        MyClass a = new MyClass(2);
        MyClass b = new MyClass(20);

        WeakReference<MyClass> aRef = new WeakReference<>(a), bRef = new WeakReference<>(b);
        a = null;// no more Strong references to a
        b = null;// no more Strong references to b

        System.gc();
        if(aRef.get() == null) System.out.println("a collected");
        if(bRef.get() == null) System.out.println("b collected");
    }

    Runnable r;
    public MyClass(int i) {
        if (i < 10) {
            r = () -> System.out.println(i);
        } else {
            r = () -> {
                // reference from Runnable to MyClass is no problem
                System.out.println(format(i));
            };
        }
    }

    private String format(Integer i) {
        return "(" + i + ")";
    }
}

Вы можете поместить эти связанные объекты в слабую хэш-карту как keys , чтобы позволить им собирать мусор, что, конечно, произойдет, только когда конкретный MyClass экземпляр, который все еще содержит в себе сильную ссылку, получает сборщик мусора:

public class MyClass {
    public static void main(String[] args) {
        MyClass a = new MyClass(2);
        MyClass b = new MyClass(20);

        for(Runnable r: REGISTERED) r.run();

        System.out.println("cleaning up");

        a = null;// no more Strong references to a
        b = null;// no more Strong references to b

        System.gc();

        // empty on common JRE implementations
        for(Runnable r: REGISTERED) r.run();
    }

    static Set<Runnable> REGISTERED = Collections.newSetFromMap(new WeakHashMap<>());

    Runnable r;
    public MyClass(int i) {
        r = i < 10?
            () -> System.out.println(i):
            () -> {
                // reference from Runnable to MyClass is no problem
                System.out.println(format(i));
            };
        REGISTERED.add(r);
    }

    private String format(Integer i) {
        return "(" + i + ")";
    }
}

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

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

Но вы можете столкнуться с проблемами и в другом направлении.Ваш вопрос предполагает, что возможно было бы написать ваших слушателей (Runnable в примере) таким образом, чтобы они не содержали ссылок на экземпляр, время жизни которого должно определять время жизни слушателя (экземпляр MyClass),Это поднимает вопрос, каким образом времена жизни этих объектов связаны вообще.Вы должны сохранять сильные ссылки на эти ключевые объекты, чтобы поддержать этих слушателей, что также подвержено ошибкам.

0 голосов
/ 21 сентября 2018

API-интерфейс Reflection предоставляет вам доступ ко всем полям объекта времени выполнения (и его типа времени выполнения, и, возможно, объекта Class).Теоретически, вы можете пройти через дерево полей вашего экземпляра (и статических полей в классе), поля полей и т. Д.

Хотя это возможно, я сомневаюсь, что это будет осуществимо.Вы пишете, что не заботитесь о производительности, но она может быть слишком медленной для запуска разработки.Что приводит нас к правилу 1 реализации собственного кэша: не делайте этого.

...