Измерение времени создания и уничтожения простого объекта

Из эффективного Java 2-го издания, пункт 7: избегайте финализаторов

"О, и еще одно: использование финализаторов серьезно снижает производительность. На моей машине время создания и уничтожения простого объекта составляет около 5,6 нс. Добавление финализатора увеличивает время до 2400 нс. Другими словами, создание и уничтожение объектов с финализаторами примерно в 430 раз медленнее."

Как можно измерить время создания и уничтожения объекта? Вы просто делаете:

long start = System.nanoTime();
SimpleObject simpleObj = new SimpleObject();
simpleObj.finalize();
long end = System.nanoTime();
long time = end - start;

person Lydon Ch    schedule 03.04.2010    source источник


Ответы (4)


Это измеряет только время выполнения метода finalize. Подавляющее большинство затрат на доработку будет связано со специальной обработкой, которую должен выполнить GC.

person Stephen C    schedule 03.04.2010
comment
хорошо, а что, если я включу System.gc() сразу после simpleObj.finalize() выше? - person Lydon Ch; 03.04.2010
comment
И циклы, потраченные на усложнение GC. Вы не можете провести микробенчмаркинг. - person Tom Hawtin - tackline; 03.04.2010
comment
@Tom Тогда как Джошуа Блох измерил время, указанное в книге? - person Lydon Ch; 03.04.2010
comment
@portoalet У меня нет с собой книги. Однако Джош работал над Java в Sun и Google. Я предполагаю, что есть несколько больших систем для тестирования. (В NIO финализаторы удалены в 1.4.1, IIRC, потому что они вызывали проблемы с производительностью.) - person Tom Hawtin - tackline; 03.04.2010
comment
хм, ладно, думаю, мне просто нужно спросить Джошуа, как он проводил измерения... Дело в том, что я не могу найти его адрес электронной почты? - person Lydon Ch; 03.04.2010
comment
Отсюда: [weblogs.java.net/blog/2006 /05/04/understanding-weak-references] это можно измерить с помощью ReferenceQueue и PhantomReference, но как прослушать, когда что-то попадает в ReferenceQueue? Я не знаю - person Jaime Hablutzel; 21.07.2012

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

Этот подход является лучшим во многих ситуациях, когда код, который вы пишете, не является единственным задействованным кодом, когда вы зависите от системных вызовов или внешних ресурсов.

Думаю, в данном случае это сделал Джошуа Блох, но моя копия наверху, а мне лень.

person CPerkins    schedule 03.04.2010
comment
Ну, в книге не сказано, как Джош проводит микробенчмарк. То есть, по сути, вы говорите, что поместите приведенный выше код в цикл for, а затем усредните время? - person Lydon Ch; 03.04.2010
comment
Ага. Сделайте все возможное, чтобы исключить любые другие затраты на установку, загрузку классов и т. д. (например, создайте и обнулите несколько экземпляров перед началом отсчета времени). - person CPerkins; 05.04.2010
comment
а как вы тестируете реальное уничтожение объектов??? Насколько я знаю, спецификация JVM даже не требует реализаций для завершения всех объектов, или, может быть, я ошибаюсь. - person Jaime Hablutzel; 21.07.2012
comment
@jaime Это сделать сложнее (возможно, невозможно для отдельных объектов) способом, который можно сравнить с финализатором и без финализатора. Но если делать много-много объектов в течение длительного периода времени, вес финализатора должен стать очевидным. - person CPerkins; 26.07.2012

Вы можете запросить у среды выполнения сборку мусора через Runtime.gc(), но сборщик мусора не обязан выполнять это тут же:

  • Метод finalize() вызываться не вами, а сборщиком мусора
  • Вы можете просто установить simpleObj в null, а затем вызвать сборку мусора, чтобы добиться более реалистичного сценария.
person EdSG    schedule 03.04.2010

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

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

PhantomReference ссылки, ближайшие к концу жизненного цикла объекта. Другими словами, объект фантомно доступен.

public class WithoutFinalizationV1 {
    
    public static void main(String[] args) {
        ReferenceQueue<WithoutFinalizationV1> queue = new ReferenceQueue<>();
        long start = System.nanoTime();
            
        PhantomReference<WithoutFinalizationV1> rf = new PhantomReference<>(
        new WithoutFinalizationV1(), queue);
        System.gc(); //advise JVM to do GC
            
        Object x = null;
        int waitCount = -1;
        do{
            x = queue.poll();
            waitCount++;
        } while(x == null);
            //only need this time point
        System.out.println("WithoutV1 "+ waitCount + " " + (System.nanoTime() - start));
    }
}

Запустив несколько раз, мировой рекорд составляет 5394860 нс, что далеко от 5,6 нс.

После добавления

@Override
protected void finalize() throws Throwable {
}

результат 5632208ns.

Вот выдержка из связанного поста, который я написал.

person MuZi    schedule 22.07.2021