Здравствуйте, Airat Burganov, Вы писали:
AB>>>... Создание объектов в java довольно дешевое.
Т>>это утверждение ложно и по памяти и по рессурсам CPU даже для такого банального класса как Object, не говоря про создание экземпляров более сложных классов. Т>>это написано не только в продвинутых трудах по оптимизации, но и в букварях по JAVA. AB>ссылки плиз.
мне кажется Вы умышленно недооцениваете свои возможности
ну эти то статьи уже не аргументы, длаго на дваре 2006 (против 2000 в там) и в прадакшене Java SE 5.0, против 1.2.2 d тут. Так что не актуально.
А по существу — опять все зависит от существа. Если у меня веб-приложение, интерфейс к БД или подобное тому — я забью на все эти оптимизации и буду созавать столько объетов, сколько мне надо. Дешевле потом будет добавить один хост в кластер. Если у меня приложение с высокой нагрузкой, у которого просят данные со скоростью до 100 запрсов в секунду — то тут будет важен каждый вновь созданный объект.
Здравствуйте, dshe, Вы писали:
D>А что делать, когда констант не хватит?
попросить Sun переписать java
D>И как контролировать то, что одна и те же константа не используется для разных целей?
не совсем понял как ты еще намереваешься использовать типы событий? петь псалмы по ним?
Здравствуйте, tavr, Вы писали:
T>Здравствуйте, dshe, Вы писали:
D>>А что делать, когда констант не хватит? T>попросить Sun переписать java
D>>И как контролировать то, что одна и те же константа не используется для разных целей? T>не совсем понял как ты еще намереваешься использовать типы событий? петь псалмы по ним?
Пример: Один разработчик для какого-то своего типа события FOO зарезервировал константу (1<<8), и другой разработчик, ничего не зная о первом, уже для своего события BAR зарезервировал константу (1<<8). В результате этого получается, что одновременно использовать события FOO и BAR невозможно из-за коллизии констант. Мой вопрос был о том, как избежать подобной ситуации?
Здравствуйте, dshe, Вы писали:
D>Пример: Один разработчик для какого-то своего типа события FOO зарезервировал константу (1<<8), и другой разработчик, ничего не зная о первом, уже для своего события BAR зарезервировал константу (1<<8). В результате этого получается, что одновременно использовать события FOO и BAR невозможно из-за коллизии констант. Мой вопрос был о том, как избежать подобной ситуации?
предполагаю, что там события контекстны. чтобы быть более понятным
Здравствуйте, dshe, Вы писали:
D>А что делать, когда констант не хватит?
Сложный вопрос. Мне кажется что в SWT он решен тем что написание собственных виджетов на столько трудоемко что этим никто не занимается. к тому же набор низкоуровневых событий UI ограничен.
D>И как контролировать то, что одна и те же константа не используется для разных целей?
Оу, ну для этого достаточно все константы хранить в одном классе. В SWT он так и называется: SWT.
Здравствуйте, Тычеблин, Вы писали:
AB>>>>... Создание объектов в java довольно дешевое.
Т>>>это утверждение ложно и по памяти и по рессурсам CPU даже для такого банального класса как Object, не говоря про создание экземпляров более сложных классов. Т>>>это написано не только в продвинутых трудах по оптимизации, но и в букварях по JAVA.
Т>дело в том что ситуация с тех далеких пор не сильно-то и изменилась. Т>и вчера и сегодня и завтра останется верным следущее утверждение Т>среди всех основных операций, ну там сложение примитивов, обращение к элементу массива.... создание объектов рессурсоемко.
чтобы весь флейм перестал быть голословным надо бы, конечно, найти информацию о конкретных алгоритмах управления памятью в современных JVM
но это сложно, особенно, не будучи сильно в теме. взамен я бы порекомендовал почитать хорошую книгу по разнообразному управлению памятью в C++, там где аллокаторы описываются и перегрузка операций new/delete. естественно, не для того, чтобы стать гуру по C++, а просто чтобы понять, что в основе эффективного управления памятью (создание и удаление объектов) также лежит небольшое количество простых операций.
собственно, ключевой момент в том, что тяжелая и ресурсоемкая часть операции выделения памяти производится не тогда, когда в коде встречается new, а заранее. при new отрабатывает легкий алгоритм поиска подходящего большого блока памяти с последующим инкрементом указателя на его свободную часть (я не имею в виду какую-то конкретную JVM, я намекаю на общий подход), и то и другое — немного арифметики для CPU.
Здравствуйте, Денис Цыплаков, Вы писали:
ДЦ>------------------------ ДЦ>Результаты на CPU 2 ГГц RAM 1Гб ДЦ>------------------------ ДЦ>кол-во создаваемых объектов: 100000 ДЦ>String Factory : 219 ms. ДЦ>Custom bean Factory : 63 ms.
ДЦ>Меня такая скорость устраивает.
ИМХО, основные затраты производительности при создании большого количества временных объектов (коими является массив слушателей определенного типа) уходят на отработку GC, а не на само создание.
Здравствуйте, Blazkowicz, Вы писали:
B>ИМХО, основные затраты производительности при создании большого количества временных объектов уходят на отработку GC, а не на само создание.
тоже не факт. в том смысле, что GC же не собирает их по одному, а только как-то помечает. после очередного периода анализа все меченые одним махом удаляются (например, изменением статуса их общего блока или инкрементом указателя)
все-таки в контексте создания большого количества объектов имхо самое ресурсоемкое — это их конструкторы, если таковые имеют место быть и делать что-то менее тривиальное, чем пара-тройка переприсваиваний параметров
B>большого количества временных объектов (коими является массив слушателей определенного типа)
и часто такой массив велик? возьмем банальную кнопку, сколько в среднем слушателей ее нажатия?
Здравствуйте, C0s, Вы писали:
B>>ИМХО, основные затраты производительности при создании большого количества временных объектов уходят на отработку GC, а не на само создание.
C0s>тоже не факт. в том смысле, что GC же не собирает их по одному, а только как-то помечает. после очередного периода анализа все меченые одним махом удаляются (например, изменением статуса их общего блока или инкрементом указателя)
Ну, дык анализ ведь время отниамает а не dispose.
C0s>все-таки в контексте создания большого количества объектов имхо самое ресурсоемкое — это их конструкторы, если таковые имеют место быть и делать что-то менее тривиальное, чем пара-тройка переприсваиваний параметров
Давайте не отвлекатся от темы и обсуждать только наш случай?
B>>большого количества временных объектов (коими является массив слушателей определенного типа) C0s>и часто такой массив велик? возьмем банальную кнопку, сколько в среднем слушателей ее нажатия?
GC плевать на размер объектов, производительность падает от сложности и размеров дерева объектов.
Blazkowicz пишет:
> ДЦ>Меня такая скорость устраивает. > > ИМХО, основные затраты производительности при создании большого > количества временных объектов (коими является массив слушателей > определенного типа) уходят на отработку GC, а не на само создание.
Я этот вопрос для себя изучал довольно тщательно. Дело в том, что
мы пишем софт для опроса различного рода устройств, а там задержки
могут быть очень критичны. Например в счетчиках электроэнергии
семейства ABB "Альфа" на авторизацию дается ~500 мс.
Причем надо проделать 2 транзакции запрос/ответ.
Соответственно я тщательно смотрел — где могут быть грабли. Не
хотелось попасть в ситуацию, когда софт сервера сбора написан на
Яве, а в силу ограничений на быстродействие его в принципе на Яве
написать нельзя.
Так вот. GC в современных Ява машинах
1. Довольно быстрый, плюс есть такая вещь как инкрементальный GC
коорый для многих задач решает проблему на корню.
2. Многопотоковый. Т.е. когда наступает GC тормозятся не все потоки,
а только тот которому надо памяти, соответсвенно если критичные
нити много пямяти не кушают, а в идеале вообще новых объектов не
создают, то GC их работу не тормозит.
3. Слухи про тормознутость Явы сильно преувеличены. Некоторые вещи
в ней работают быстрее чем в Си. Видимо за счет оптимизации при
JIT компиляции (правда таких вещей ОЧЕНЬ мало, но факт имеет место
быть)
Сама платформа как таковая работает довольно шустро. Мне для моих
довольно критичных ко времени задач — хватает. НО. Если решения
которые можно реализовать как на Яве, так и на Си, которые памяти
хотят много. И часто именно таким решениям приписывают тормоза.
Если изначально архитектура спроектированна так, что затратных
элементов в ней нет, то все будет работать быстро. При этом
мелкая оптимизация циклов, выделения памяти и т.п. не имеет большого
значения и ею можно и нужно пожертвовать в угоду удобочитаемости
кода и легкости последующей модификации проекта.
--
WBR Денис Цыплаков /* jabber UID: denis.tsyplakov@jabber.ru */
Знающий не говорит, говорящий не знает
Здравствуйте, Денис Цыплаков, Вы писали:
ДЦ> Так вот. GC в современных Ява машинах ДЦ> 1. Довольно быстрый, плюс есть такая вещь как инкрементальный GC ДЦ> коорый для многих задач решает проблему на корню.
Согласен.
ДЦ> 2. Многопотоковый. Т.е. когда наступает GC тормозятся не все потоки, ДЦ> а только тот которому надо памяти, соответсвенно если критичные ДЦ> нити много пямяти не кушают, а в идеале вообще новых объектов не ДЦ> создают, то GC их работу не тормозит.
Дык мы тут и имеем новые массивы при каждом обращении к методу. Выходит эти потоки все же будут блокироватся?
Здравствуйте, Blazkowicz, Вы писали:
ДЦ>> 2. Многопотоковый. Т.е. когда наступает GC тормозятся не все потоки, ДЦ>> а только тот которому надо памяти, соответсвенно если критичные ДЦ>> нити много пямяти не кушают, а в идеале вообще новых объектов не ДЦ>> создают, то GC их работу не тормозит. B>Дык мы тут и имеем новые массивы при каждом обращении к методу. Выходит эти потоки все же будут блокироватся?
к тому же "эти потоки" — это ивент-диспатчер поток, то есть вероятно появление рывков в интерфесе.
Здравствуйте, Lucker, Вы писали:
B>>Дык мы тут и имеем новые массивы при каждом обращении к методу. Выходит эти потоки все же будут блокироватся?
L>к тому же "эти потоки" — это ивент-диспатчер поток, то есть вероятно появление рывков в интерфесе.
Blazkowicz пишет:
> ДЦ> 2. Многопотоковый. Т.е. когда наступает GC тормозятся не все потоки, > ДЦ> а только тот которому надо памяти, соответсвенно если критичные > ДЦ> нити много пямяти не кушают, а в идеале вообще новых объектов не > ДЦ> создают, то GC их работу не тормозит. > Дык мы тут и имеем новые массивы при каждом обращении к методу. Выходит > эти потоки все же будут блокироватся?
Дело в том, что релаьно сборка мусора может происходить в соседней
нити. А когда придет очередь нашей нити просить памяти — то у VM
уже будет N-цать мегабайт освобожденные пока ждала соседняя нить.
Это конечно как повезет, но в многопотоковой программе, сильно
облегчает жизнь.
Вот я тут сделал тестик иллюстрирующий работа GC. Тест на самом деле
не шибко показательный, т.к. объекты тут-же освобождаются и плюс
к тому, они одинакового размера, нет винигрега в куче. Но что-то
из него понять таки можно.
Тест запускался с опцией -Xmx1M т.е. JVM давался один мегабайт
памяти.
package ru.rtec.test.gc;
public class TestGC
{
static long TEST_LENGTH = 15 * 1000;
static int blockSize;
public static void main(String[] args)
{
for (blockSize = 256; blockSize < 512 * 1024;
blockSize = blockSize * 2)
{
System.out.println("---\nBlockSize: " + blockSize + " b");
test();
}
}
private static void test()
{
long maxTime = 0;
long testStart = System.currentTimeMillis();
long allocCount = 0;
while (System.currentTimeMillis() - testStart < TEST_LENGTH)
{
long startCreate = System.currentTimeMillis();
byte[] data = new byte[blockSize];
long cLength = System.currentTimeMillis() - startCreate;
data[0] = 1;//обманем оптимизатор на всякий случай.
//16 мс - OS context switchif ((cLength!= 16)&&(cLength > maxTime))
{
maxTime = cLength;
}
allocCount++;
}
System.out.println("Max time: " + maxTime + " ms");
System.out.println("Allocation count: " + allocCount);
System.out.println(
"Total allocated: " +
allocCount * blockSize / 1024 / 1024 + " Mb");
}
}
Вот результаты
java -Xmx1M ru.rtec.test.gc.TestGC
---
BlockSize: 256 b
Max time: 16 ms
Allocation count: 31000048
Total allocated: 7568 Mb
---
BlockSize: 512 b
Max time: 32 ms
Allocation count: 19902126
Total allocated: 9717 Mb
---
BlockSize: 1024 b
Max time: 31 ms
Allocation count: 12387256
Total allocated: 12096 Mb
---
BlockSize: 2048 b
Max time: 31 ms
Allocation count: 6646271
Total allocated: 12980 Mb
---
BlockSize: 4096 b
Max time: 16 ms
Allocation count: 3564556
Total allocated: 13924 Mb
---
BlockSize: 8192 b
Max time: 31 ms
Allocation count: 1871281
Total allocated: 14619 Mb
---
BlockSize: 16384 b
Max time: 16 ms
Allocation count: 946896
Total allocated: 14795 Mb
---
BlockSize: 32768 b
Max time: 16 ms
Allocation count: 484477
Total allocated: 15139 Mb
---
BlockSize: 65536 b
Max time: 32 ms
Allocation count: 253517
Total allocated: 15844 Mb
---
BlockSize: 131072 b
Max time: 16 ms
Allocation count: 126876
Total allocated: 15859 Mb
---
BlockSize: 262144 b
Max time: 31 ms
Allocation count: 52479
Total allocated: 13119 Mb
Не забываем, что на 16 мс. время от времени процесс отрубается
Windows
--
WBR Денис Цыплаков /* jabber UID: denis.tsyplakov@jabber.ru */
Знающий не говорит, говорящий не знает
Здравствуйте, Денис Цыплаков, Вы писали:
ДЦ> Дело в том, что релаьно сборка мусора может происходить в соседней ДЦ> нити. А когда придет очередь нашей нити просить памяти — то у VM ДЦ> уже будет N-цать мегабайт освобожденные пока ждала соседняя нить.
ДЦ> Это конечно как повезет, но в многопотоковой программе, сильно ДЦ> облегчает жизнь.
В AWT/SWT поток один. Подозреваю что это так во многих других UI системах.
В остальном сдаюсь, leak of performance не может быть вызван именно этими массивами.
Вот в тест в котором выделяются блоки разного размера,
причем не все они сразу удаляются, в каждый второй некоторое
время хранится на куче.
Имитируется фрагментация кучи. Тоже конечно так себе но уже
ближе к реальной жизни. Кол-во итераций и время каждой
итерации уменьшено — поздно домой уже пора идти.
package ru.rtec.test.gc;
import java.util.*;
public class TestGC
{
static long TEST_LENGTH = 1 * 1000;
static int blockSize;
static Queue<byte[]> queue = new LinkedList<byte[]>();
static int queueSeed = 1;
public static void main(String[] args)
{
for (blockSize = 256; blockSize <= 64 * 1024;
blockSize = blockSize * 2)
{
System.out.println("---\nBlockSize: " + blockSize + " b");
test();
}
}
private static void test()
{
long maxTime = 0;
long testStart = System.currentTimeMillis();
long allocCount = 0;
while (System.currentTimeMillis() - testStart < TEST_LENGTH)
{
long startCreate = System.currentTimeMillis();
byte[] data = new byte[blockSize];
long cLength = System.currentTimeMillis() - startCreate;
if (queueSeed % 2 == 1)
{
queue.add(data);
if (queue.size() > 20 )
{
queue.remove();
}
}
queueSeed++;
if ( queueSeed > blockSize - 1 )
{
queueSeed = 1;
}
if (cLength > maxTime)
{
maxTime = cLength;
}
allocCount++;
}
System.out.println("Max time: " + maxTime + " ms");
System.out.println("Allocation count: " + allocCount);
System.out.println(
"Total allocated: " +
allocCount * blockSize / 1024 / 1024 + " Mb");
}
}
Вот результаты. Как видно они не шибко отличаются от предидущего теста.
---
BlockSize: 256 b
Max time: 16 ms
Allocation count: 1640605
Total allocated: 400 Mb
---
BlockSize: 512 b
Max time: 16 ms
Allocation count: 1126191
Total allocated: 549 Mb
---
BlockSize: 1024 b
Max time: 16 ms
Allocation count: 705933
Total allocated: 689 Mb
---
BlockSize: 2048 b
Max time: 16 ms
Allocation count: 386814
Total allocated: 755 Mb
---
BlockSize: 4096 b
Max time: 31 ms
Allocation count: 114607
Total allocated: 447 Mb
---
BlockSize: 8192 b
Max time: 32 ms
Allocation count: 22563
Total allocated: 176 Mb
---
BlockSize: 16384 b
Max time: 47 ms
Allocation count: 5669
Total allocated: 88 Mb
---
BlockSize: 32768 b
Max time: 47 ms
Allocation count: 1906
Total allocated: 59 Mb
---
BlockSize: 65536 b
Max time: 32 ms
Allocation count: 402
Total allocated: 25 Mb
--
WBR Денис Цыплаков /* jabber UID: denis.tsyplakov@jabber.ru */
Знающий не говорит, говорящий не знает
Blazkowicz пишет:
> В AWT/SWT поток один. Подозреваю что это так во многих других UI системах.
Я знаю. Но в реальной жизни, если разработчик не хочет, чтобы при
каждой блокирующей операции программа серела мордой, то собственно
потоков в программе много. Например у нас принято, что ВСЕ RMI
вызовы — только в отдельном потоке (Callable<ResultType> если
говорить конкретно). Это же касается отчетов и длинных операций с БД
--
WBR Денис Цыплаков /* jabber UID: denis.tsyplakov@jabber.ru */
Знающий не говорит, говорящий не знает
Здравствуйте, Денис Цыплаков, Вы писали:
>> В AWT/SWT поток один. Подозреваю что это так во многих других UI системах.
ДЦ> Я знаю. Но в реальной жизни, если разработчик не хочет, чтобы при ДЦ> каждой блокирующей операции программа серела мордой, то собственно ДЦ> потоков в программе много. Например у нас принято, что ВСЕ RMI ДЦ> вызовы — только в отдельном потоке (Callable<ResultType> если ДЦ> говорить конкретно). Это же касается отчетов и длинных операций с БД
Ну, вот и выходит то же самое что и в Swing, если это наш кастомный проект, то мы об этом можем не заботится особо. Ежели это framework который будет использоватся сторонними лицами, то тут нам и стоит задуматся даже о незначительных оптимизациях производительности.