Конфигурация на основе весенних аннотаций - слишком высокое потребление памяти?

Когда я заметил сумасшедшее использование ОЗУ в моем клиентском приложении (на основе Swing), я начал изучать его, и похоже, что это как-то связано с конфигурацией на основе аннотаций в Spring. Как вы увидите в моих правках ниже, я понял, что это происходит только на 64-битной JVM.

См. следующий тестовый код:

конфигурация на основе XML

<beans ....>
     <bean id="xmlConfigTest" class="at.test.XmlConfigTest" />
</beans>

public class XmlConfigTest extends JFrame {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
        XmlConfigTest frame = (XmlConfigTest) ctx.getBean("xmlConfigTest");
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

Использует около 32 МБ памяти, что мне кажется нормальным.

Теперь то же самое с настройкой на основе аннотаций:

@Service
public class AnnotationConfigTestFrame extends JFrame {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("at.test");

        AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
            .getBean("annotationConfigTestFrame");
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
       frame.setVisible(true);
    }
}

Мало того, что открытие фрейма занимает заметно больше времени, так еще и потребление памяти взлетает до 160 МБ памяти при запуске, а затем выравнивается примерно до 152 МБ, что мне кажется очень высоким. И помните, это только самый простой случай, клиентское приложение, которое я разрабатываю, уже потребляет более 400 МБ, что слишком много для старых машин.

У кого-нибудь есть объяснение такому поведению? Я не понимаю..

(Кстати, здесь используется 3.1.1.RELEASE.)

edit* Как было предложено axtavt, я также попытался построить AnnotationConfigApplicationContext непосредственно с тестовым классом в качестве аргумента, так что сканирование путей к классам не требуется. К сожалению, ничего не изменилось в потреблении памяти.

изменение 2 удалено, см. изменение 3

edit 3 Сейчас я тестировал на той же машине (64-разрядная версия Windows 7) как с 32-разрядной, так и с 64-разрядной JVM и тестовыми программами, указанными выше. Вот результаты:

конфигурация на основе xml:

32-Bit JVM: 16MB
64-Bit JVM: 31MB

конфигурация на основе аннотации:

32-Bit JVM: 17MB
64-Bit JVM: 160MB

Таким образом, на 32-битной JVM обе программы близки, чего я и ожидал. Однако на 64-битной версии все по-другому. Даже первая программа использует вдвое больше памяти на 64-битной, что уже кажется лишним. Тем не менее, это не имеет ничего против второй программы, которая использует почти в 10 раз больше памяти на 64-битной системе.

редактировать 4 Теперь протестировано и под Ubuntu -> тот же эффект. Однако до сих пор не знаю, почему это происходит. Это действительно нарушение сделки для меня


person Mario B    schedule 19.07.2012    source источник
comment
Можете ли вы определить, какую JVM и версию JVM вы используете?   -  person pd40    schedule 22.07.2012
comment
Пробовал с (oracle) 1.6.0_24 и (orcale) 1.7.0_03. Практически никакой разницы к сожалению   -  person Mario B    schedule 23.07.2012
comment
У меня есть одно предложение: делать дампы кучи (сразу после запуска) как на 32-битных, так и на 64-битных JVM. Сравните количество объектов, созданных в обоих экземплярах, и убедитесь, что в обоих случаях создается одинаковое количество объектов одного и того же типа. Еще одно предложение — запустить приложение с флагом CompressedOops на 64-разрядной JVM.   -  person Binil Thomas    schedule 23.07.2012
comment
@Matrium Мне не удалось воспроизвести высокое потребление памяти с помощью Spring 3.1.2.RELEASE на 64-битной JVM 1.6.0_33 для Mac OS X. На моей машине XmlConfigTest заняло 15 МБ, но AnnotationConfigTestFrame заняло всего 5М. У вас много классов в пути к классам или в пакете at.test?   -  person Binil Thomas    schedule 23.07.2012
comment
Интересный! Я не могу проверить это сам, потому что у меня нет машины с Mac OS X. Нет, проект состоит только из двух упомянутых выше классов и applicationContext.xml для теста xml-конфигурации. (и, конечно, spring-beans + spring-context + их зависимости). Скоро сделаю дамп памяти   -  person Mario B    schedule 23.07.2012
comment
Данных, которые вы предоставляете, слишком мало, чтобы делать разумные выводы. Если вы используете JVisualVM или другие инструменты, вы сможете увидеть, что занимает место.   -  person Edmondo1984    schedule 23.07.2012
comment
@Matrium Я беру свои слова назад - я вижу большое использование памяти как в Linux, так и в OS X. См. pastie.org/4307910 и pastie.org/4307915.   -  person Binil Thomas    schedule 23.07.2012


Ответы (2)


При запуске создается большое количество объектов java.lang.reflect.Method.

дамп кучи

Эти объекты подходят для сборки мусора, но в случае вашего приложения это, вероятно, вызывает слишком много сборок eden, что приводит к большому времени запуска.

Большинство из этих java.lang.reflect.Method объектов расположены на следующем сайте:

сайты размещения для объектов java.lang.reflect.Method

Кажется, они создаются, когда Spring пытается найти сеттеры в AnnotationConfigTestFrame, который наследует множество методов из суперклассов java.awt и javax.swing. Я не внимательно читал соответствующий код, но в качестве быстрого теста для проверки этой гипотезы я сделал следующее:

@Service
public class AnnotationConfigTestFrame /* extends JFrame */
{
    public static void main(String[] args) throws InterruptedException
    {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfigTestFrame.class);

        AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
                .getBean("annotationConfigTestFrame");
//        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
//        frame.setVisible(true);

        waitABit();
        printRuntimeStats();
        System.exit(0);
    }
}

то есть сделал AnnotationConfigTestFrame не наследовать от javax.swing.JFrame. Теперь использование памяти для поиска bean-компонента достаточно низкое!

Это может дать вам подсказки для дальнейшей отладки.

person Binil Thomas    schedule 23.07.2012
comment
Это было действительно полезно, спасибо! Думаю, с этого момента я буду использовать xml-конфигурацию для своего пакета графического интерфейса вместо сканирования. - person Mario B; 24.07.2012

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

Если вы хотите избежать сканирования пути к классам, вы можете вместо этого попытаться предоставить точный набор аннотированных классов (@Components и @Configurations), используя соответствующий конструктор AnnotationConfigApplicationContext.

person axtavt    schedule 19.07.2012
comment
Я только что попробовал это и построил это так: new AnnotationConfigApplicationContext(AnnotationConfigTestFrame.class); Это сработало, не изменило мысль о потреблении памяти. - person Mario B; 19.07.2012