Пропуск поля экземпляра во время выполнения в Java - PullRequest
0 голосов
/ 25 сентября 2018

Механизм Java assert позволяет отключить вставку утверждений, которые по существу не требуют затрат времени выполнения (кроме файла большего размера), если утверждения отключены.Но это может охватывать все ситуации.

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

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

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

class MyClass {
    static final boolean useRefCounts = my.global.Utils.useRefCounts();
    static {
        if(useRefCounts)
            int refCount; // want instance field, not local variable
    }
    void incrementRefCount(){
        if(useRefCounts) refCount++; // only use field if it exists;
    }
    /**return true if ready to be collected and reused*/
    boolean decrementAndTestRefCount(){
        // rely on Java's garbage collector if ref counting is disabled.
        return useRefCounts && --refCount == 0;
    }
}

Проблема с приведенным выше кодом заключается в том, чтостатический бок не имеет смысла.Но есть ли какая-то хитрость с использованием маломощной магии, чтобы заставить что-то в этом духе работать?(Если разрешена мощная магия, ядерная опция - это сгенерировать две версии MyClass и договориться поставить правильную на пути к классам во время запуска.)

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Если вы принимаете немного более высокие накладные расходы в случае, если вы используете количество ссылок, вы можете прибегнуть к внешнему хранилищу, например:

class MyClass {
    static final WeakHashMap<MyClass,Integer> REF_COUNTS
        = my.global.Utils.useRefCounts()? new WeakHashMap<>(): null;

    void incrementRefCount() {
        if(REF_COUNTS != null) REF_COUNTS.merge(this, 1, Integer::sum);
    }
    /**return true if ready to be collected and reused*/
    boolean decrementAndTestRefCount() {
        return REF_COUNTS != null
            && REF_COUNTS.compute(this, (me, i) -> --i == 0? null: i) == null;
    }
}

Существует поведенческая разница для случая, когда кто-то вызывает decrementAndTestRefCount() чаще incrementRefCount().В то время как ваш исходный код молча сталкивается с отрицательным числом ссылок, этот код выдаст NullPointerException.В этом случае я предпочитаю сбои с исключением…

Приведенный выше код оставит вам накладные расходы на одно поле static, если вы не используете эту функцию.У большинства JVM не должно быть проблем с устранением условий, касающихся состояния переменной static final.

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

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

ПРИМЕЧАНИЕ. Возможно, вам вообще не понадобится это делать.JIT очень хорош для встраивания констант, известных во время выполнения, особенно boolean, и оптимизации кода, который не используется.

Поле int не является идеальным, однако, если вы используете 64-битную JVM, размер объекта может не измениться.

В JJM OpenJDK / Oracle (64-битная)заголовок по умолчанию составляет 12 байт.Выравнивание объекта составляет 8 байт, поэтому объект будет использовать 16 байт.Поле добавляет 4 байта, что после выравнивания также составляет 16 байтов.


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

class MyClass {
    static final boolean useRefCounts = my.global.Utils.useRefCounts();

    public static MyClass create() {
        return useRefCounts ? new MyClassPlus() : new MyClass();
    }

    void incrementRefCount() {
    }

    boolean decrementAndTestRefCount() {
        return false;
    }
}

class MyClassPlus extends MyClass {
    int refCount; // want instance field, not local variable

    void incrementRefCount() {
        refCount++; // only use field if it exists;
    }

    boolean decrementAndTestRefCount() {
        return --refCount == 0;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...