LINUX.ORG.RU

[java] Бить ли за new по рукам?

 


0

0

Иначе говоря, в стандартных в том числе библиотеках, на каждый пук создается по туче объектов, как событий, так и толпы копий не иммутабельных классов а-ля Point и т.п. Вот и интересно мне насколько сильный от всего этого безобразия оверхед, и что делать.

★★☆
Ответ на: комментарий от madcore

в яве классы в своих файлах, директории отражают их иерархию //К.О.

ЖЖОШЬ

iZEN ★★★★★
()
Ответ на: комментарий от lester

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

Legioner ★★★★★
()
Ответ на: комментарий от iZEN

> Resizeable — это интерфейс, а не класс. Очнись, мы о Java говорим, а не о C++.

от того, что ты назвал абстрактный класс интерфейсом ровно ничего не поменялось

Код рабочий. С защитой от левых данных. Можешь сам проверить.


это п-ц, от каких «левых данных»? человек получил Resizeable и будет из астрала узнавать, что можно, а что нельзя?

Это да. Я решил показать, что может делать с наследованием, кто с ним столкнётся


что может сделать быдлокодер с простой задачей, скорее

Абсолютно логичное — исключение словит клиент там, где попытается использовать квадрат в качестве разностороннего прямоугольника.


ты накуя интерфейс ввел? чтоб все кто его используют из того же libastral узнавали, что это квадрат?

Кто указал? Ты что ли?


нет, тебе указали на чисто жабовскую реализацию

По-моему, весь примерный код по теме писали только я и qnikst, который высказывал хоть какие-то сомнения.


я тебе сразу сказал как надо - у тебя возражения есть? я если что не к коду придирался

Ты не привёл ни строчки правильного с твоей точки зрения кода, что вообще можно с тобой обсуждать?


я тебе написал названия методов, названия классов ты знаешь, что внутри - и так понятно, тебе что-то осталось непонятно?

lester ★★★★
()
Ответ на: комментарий от Legioner

> значит создание объекта в куче примерно в полтора раза дольше, чем два инкремента int-а

а удаление? в C++ мы одним легким движением почистим за собой

lester ★★★★
()
Ответ на: комментарий от Dimanc

> Так что, все жаба-кодеры делают, чтобы это выглядело запутанно и непонятно

Это называется «пакеты». В /usr/include у тебя тоже не все в одну кучу свалено

annulen ★★★★★
()
Ответ на: комментарий от lester

Resizeable — это интерфейс, а не класс. Очнись, мы о Java говорим, а не о C++.

от того, что ты назвал абстрактный класс интерфейсом ровно ничего не поменялось


Ну всё. С тобой всё ясно.

я тебе сразу сказал как надо - у тебя возражения есть? я если что не к коду придирался


КАК надо? (Мы код обсуждаем, вообще-то, а не личности.)

я тебе написал названия методов, названия классов ты знаешь, что внутри - и так понятно, тебе что-то осталось непонятно?


Мне — нет. А тебе, я вижу, всё равно — лишь бы не по теме.

iZEN ★★★★★
()
Ответ на: комментарий от iZEN

ты просто фееричен, подавайся сразу в лисперы - они все такие как ты

lester ★★★★
()
Ответ на: комментарий от lester

а удаление? в C++ мы одним легким движением почистим за собой

Ну почисти одним лёгким движением граф из ссылок объектов друг на друга. Ага — можешь сразу предлагать свою идею писателям JVM, может они воспримут её и включат в будущий GC, а то как-то хреново Java работает — всё время память жрёт как свинья помои.

iZEN ★★★★★
()
Ответ на: комментарий от Legioner

Для справки. В Java оверхед по памяти на один созданный объект порядка 80 байтов.

iZEN ★★★★★
()
Ответ на: комментарий от iZEN

> Ну почисти одним лёгким движением граф из ссылок объектов друг на друга

я тебе открою большой секрет как используются пулы:

создается пул и отдается функции, где например отрабатывает парсер, он строит те самые деревья, после чего возвращает нам только то, что мы хотим, а весь мусор, оставшийся от работы, все объекты с ссылками друг на друга, которые были созданы в процессе, просто сметается и все; а все это потому-что С++ дает возможность на самом низком уровне работать с памятью

lester ★★★★
()
Ответ на: комментарий от lester

а в яве явамашина выделяет память страницами заранее, так что при new объект появляется в куче, но уже в выделенном блоке, что очень сильно ускоряет процесс. Плюс gc удаляет старые объекты одним махом, в отличии от непереопределённого delete в сях. Так, что при выделении объектов в куче java скорее всего выиграет.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

> Плюс gc удаляет старые объекты одним махом

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

lester ★★★★
()
Ответ на: комментарий от qnikst

я не рассматриваю «умные» реализации частных случаев работы с объектами, там на cpp потенциал оптимизации неизмеримо выше.

qnikst ★★★★★
()
Ответ на: комментарий от lester

всё, что дальше ЕНМИП, поскольку читал спеки я давно, а повторить, что забыл ещё не собрался.

В яве нет деструктора как тавого (из целей производительности), есть метод finalize() вызываемый перед тем, как gc удалит объект, но разработчики настоятельно рекомендуют не пользоваться, поскольку нет гарантии, что объект будет «собран» до завершения работы программы. <offtop> даже при System.gc() вызов gc не обязательно произойдёт (от фазы луны или т.п. зависит) (hate)</offtop> поэтому обычно добавляют какой-нибудь метод, который должен быть вызван вручную перед удалением ссылок на объект + try{ }finally{}, где в finally мы подчищаем лишнее типа коннектов к внешним ресурсам освобождение памяти занятой native кодом или т.п.

qnikst ★★★★★
()
Ответ на: комментарий от lester

Согласен немного ступил. Метод finalize виртуальный(хотя для явы других и нет) метод класса Object. Плюс N вызовов метода finalize(){}; /*черт знает что с ними делает оптимизатор */ не сильно затормозят систему.

qnikst ★★★★★
()
Ответ на: комментарий от lester

погуглил - по первым ссылкам, что прочитал, пишут, что метод finalize() есть и вызывается всегда, т.е. удалить например 100 000 элементов графа, просто «забыв» про них - видимо не получится, ч.т.д.

lester ★★★★
()
Ответ на: комментарий от qnikst

> Метод finalize виртуальный(хотя для явы других и нет) метод класса Object. Плюс N вызовов метода finalize(){}; /*черт знает что с ними делает оптимизатор */ не сильно затормозят систему.

но все-таки обход всех объектов и вызов метода finalize для каждого - намного медленней чем просто отметить память как свободную

lester ★★★★
()
Ответ на: комментарий от Legioner

Ява компилятор не дурак, он радостно анбокснет Integer в int поработает с ним, и потом (если надо) забоксит обратно. Забавно будут выглядеть такие «классические» ошибки типа String s=new String();for(i..){ s+=«»+i"; } или операции с объектами List или т.п. где происходит вечное преобразование от скалярных типов к ссылочным и назад (пример как написать криво что-то в голову не приходит)

qnikst ★★★★★
()
Ответ на: комментарий от lester

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

qnikst ★★★★★
()
Ответ на: комментарий от lester

> метод finalize() есть и вызывается всегда, т.е. удалить например 100 000 элементов графа, просто «забыв» про них - видимо не получится

Метод есть всегда, но по умолчанию он пустой и вызывать его не нужно. А т.к. JVM всегда знает какие классы переопределяли этот метод, то только за объектами таких классов нужно аккуратно следить и вызывать для них finalize() во время сборки мусора. Вполне возможно, что объекты таких классов размещаются в отдельных участках памяти.

kamre ★★★
()
Ответ на: комментарий от lester

итак код:

public class Test {
    public static void main(String[] args) {
       Tester tst;
       for (int i=0; i < 100; i++) {
           tst = new Tester(i);
       }
       if ( args.length == 0 || args[0] == "gc" ) {
           System.out.println("running gc");
           System.gc();
       }
    }
}

class Tester {
    int counter;
    
    Tester(int i){
        this.counter = i;
    }

    public void finalize() throws Exception {
        System.out.println("" + counter + " was destroyed");
        //super.finalize();
    }
}

тут в finalize мы просто выводим лог. а в зависимости от программы вызываем (или не вызываем gc). Результаты:

qnikst@qnikst ~/prog/java/testFinalize $ java Test nogc
qnikst@qnikst ~/prog/java/testFinalize $ java Test nogc
qnikst@qnikst ~/prog/java/testFinalize $ java Test 
running gc
99 was destroyed
98 was destroyed
...

т.е. без принудительного вызова gc объекты тупо не успевают собраться до завершения программы (т.е. я всё таки прав). В случае если gc добрался до объекта, то finalize вызовется. + gc собирает объекты только на тех страницах памяти, которые были выделены в момент вызова gc (и туда не попадают новые объекты), соответсвенно все удалённые объекты на остальных страницах не будут затронуты. Так же важно, что в случае многопоточных программ проблема ещё больше усугубляется, подробностей я тут пока не осилил, но если кто расскажет - скажу спасибо

qnikst ★★★★★
()
Ответ на: комментарий от kamre

хорошо, а вот товарищ iZEN упоминал про тормоза при «зачистке» графов, т.к. вероятно gc удаляет все по цепочке, это так?

lester ★★★★
()
Ответ на: комментарий от kamre

Огромное спасибо за ссылку. Особенно про immutable/mutable Holders порадовало прочтение и объяснение логики работы.

qnikst ★★★★★
()
Ответ на: комментарий от iZEN

> Ну почисти одним лёгким движением граф из ссылок объектов друг на друга.

Вот «друг на друга» как раз - как нечего делать. И даже подчищать не надо, можно сразу использовать тот же пул по новой.

В чём-чём, а в управлении памятью Си и плюсы проигрывают только ассемблеру.

LamerOk ★★★★★
()
Ответ на: комментарий от qnikst

> а в яве явамашина выделяет память страницами заранее, так что при new объект появляется в куче, но уже в выделенном блоке, что очень сильно ускоряет процесс.

Можно подумать, ядро выделяет память по байту.

LamerOk ★★★★★
()
Ответ на: комментарий от qnikst

> Забавно будут выглядеть такие «классические» ошибки типа String s=new String();for(i..){ s+=«»+i"; }

Тут нет никакой ошибки, компилятор заменит этот код на StringBuffer и только по окончании цикла вернет String.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

т.е. при при работе в Cpp, ядро выделит мне заранее несколько страниц памяти, когда память выделенных подходит к концу? А при обработке new Object программа просто «скажет», что данная область памяти занята без лишних системных вызовов. при обработке delete(obj) программа сама поймёт, что не нужно вызывать дестуктор и очистит всю страницу с мусором (который она уже туда сложила) и отметит её как свободную. А в java это есть, причём изкоробки, причём заметно улучшается от верии к верии.

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

Естественно всё imho, так что с радостью выслушаю и противоположное мнение.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

> ядро выделит мне заранее несколько страниц памяти

Заранее - нет, страниц - да.

LamerOk ★★★★★
()
Ответ на: комментарий от qnikst

> днако в общем случае (сферической программы в вакууме) без специальных оптимизаций, задача выделение/освобождение памяти в java реализуется эффективнее (как по производительности, так и по безопасности ).

это все мантры, во-первых компиляторы, стандартные библиотеки и оптимизация в С/С++ тоже не пальцем писались, а во-вторых - сами программы на Java и доказывают, что это не так - как объемами потребляемой памяти, так и скоростью работы, тут даже .NET лучше

lester ★★★★
()
Ответ на: комментарий от LamerOk

oh'really?

Компилятор _может_ по своему усмотрению заменять некоторые операции со String операциями со StringBuffer происходит это далеко не всегда (к сожалению), хотя работа над улучшением идёт. Так что лучше на чудеса не надеяться.

Профукод:

public class Test {
    public static void main(String[] args) {
        if (args.length>0 && "sb".equals(args[0])) {
            System.out.println("sb");
            for (int i=0;i<2;i++){
                Test.runSb();
            }
        } else {
            System.out.println("s");
            for (int i=0; i<2; i++){
                Test.runS();
            }
        }

    }

    public static void runS(){
        String s = new String();
        for (int i=0;i<10000;i++){
            s+=""+i;
        }
        //System.out.println(s);
    }

    public static void runSb(){
        StringBuffer sb = new StringBuffer();
        for (int i=0;i<10000;i++){
            sb.append(i);
        }
        //System.out.println(sb);
    }
}

результат

qnikst@qnikst ~/prog/java/testFinalize $ time java Test sb 
sb

real	0m0.130s
user	0m0.111s
sys	0m0.031s
qnikst@qnikst ~/prog/java/testFinalize $ time java Test s
s

real	0m2.458s
user	0m2.245s
sys	0m0.217s

разница на порядок :)

qnikst ★★★★★
()
Ответ на: комментарий от lester

> это все мантры, во-первых компиляторы, стандартные библиотеки и оптимизация в С/С++ тоже не пальцем писались,

кто-бы спорил.

это все мантры, во-первых компиляторы, стандартные библиотеки и оптимизация в С/С++ тоже не пальцем писались,

такое ощущение, что я где то наверху написал, что программы на java работают быстрее и требуют меньше памяти, чем программы на cpp.. Да не писал я такого и не думаю так ;). Я написал, что выделение и освобождение памяти в jvm более дешёвая операция, чем неоптимизированные под конкретную задачу опрации на cpp. Совсем другое утверждение, да ещё и с исключением.

Ява это байткод (про JIT знаю, но всё же), в яве встречаются неконтролируеммые темы с boxing/unboxing, в яве куча дополнительных проверок привелегий, (честно хз как ява работает с adm64), для передачи параметра функции надо использовать анонимные классы (до невышедшего jdk7), на каждый чих создаются кучи объектов. Вопрос, каким психом надо быть, чтобы утверждать что это может работать лучше чем cpp?

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

а почему так медленно? я такой же пример сделал на С++:

char buf[ 10 ];
std::string s;
s.reserve( 10000000 );
for( int i = 0 ; i < 2000000 ; ++i )
{
    s += "";
    s += itoa( i, buf, 10 );
}

выполнилось за 0.109сек, но сравни до скольки у меня цикл

lester ★★★★
()
Ответ на: комментарий от wfrr

> Митан, по скорости .NET хуже.

я не говорил про тесты - я говорил про программы, сравни VC2010 с Eclipse и сам увидишь

lester ★★★★
()
Ответ на: комментарий от lester

я говорил про программы, сравни VC2010 с Eclipse и сам увидишь

Таки, митан, используйте сравнения вычислительных программ, а не сравнивайте гуйню, аки толстый тролль.

wfrr ★★☆
() автор топика
Ответ на: комментарий от lester

сделал с 2000000 StringBuffer справился за

real   0m0.497s user   0m0.356s sys   0m0.097s

Из которых сколько-то ушло на инициализацию jvm. А так медленно из-за строчки s.reserve, заменой начального размера буффера удалось сократить до 0.430s в принципе может можно и лучше, но мне лень :) А вобще тогда уже и компы померять надо, но отставание на 20-50% явы от си это в порядке вещей.

А теперь по поводу String. экземпляры класса String являются Immutable, т.е. на каждый += создаётся новый объект, куда копируется кусок станой строки и новая, инициализируются поля типа размера строки и т.п. В итоге мы получаем то, что получаем.

Вобще в яве хреновая работа со строками, что странно т.к. яву юзают в enterprise, а там работа со строками это 90% всего, что происходит.

Если я где натупил — поправляйте.

qnikst ★★★★★
()
Ответ на: комментарий от wfrr
std::vector<int>* vec = new std::vector<int>( 10000000 );
delete vec;

выполнилось за 0.011сек

for( size_t i = 0 ; i < 1000000 ; ++i )
{
	int* p = new int[ 1000 ];
	delete[] p;
}

за 2.978сек

lester ★★★★
()
Ответ на: комментарий от wfrr

> Да конечно, lester обычный шланг, или действительно он настолько -умен?

белка - пошла нах :) если ты не пишешь reserve - то просто идиот и это твои интимные проблемы

lester ★★★★
()
Ответ на: комментарий от qnikst

Вобще в яве хреновая работа со строками, что странно т.к. яву юзают в enterprise, а там работа со строками это 90% всего, что происходит.

Она не хреновая, а рвущая шаблон затеникам перешедшим с сей и дельфи и не осилившим сменить шаблоны.

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

wfrr ★★☆
() автор топика
Ответ на: комментарий от wfrr

> Таки, митан, используйте сравнения вычислительных программ, а не сравнивайте гуйню, аки толстый тролль.

http://shootout.alioth.debian.org/u32q/benchmark.php?test=all&lang=csharp&lan...

это сравнение с mono, то что реализация от M$ лучше - очевидно или нет?

lester ★★★★
()
Ответ на: комментарий от LamerOk

С Java 5.0 конкатенируемые строки заворачиваются в StringBuilder, а он потоко-небезопасны, хоть и быстрый.

iZEN ★★★★★
()
Ответ на: комментарий от lester

Таки -умен? Дело в том что для строки это буфер выделяется не сразу, а итеративно когда кончается место в текущем, и это сопровождается копирование исходного массива в новый, ога, и это может происходить на каждый инкремент, такие дела.

wfrr ★★☆
() автор топика
Ответ на: комментарий от lester

реализация от M$ лучше - очевидно или нет?

Неочевидно ( про фанатиков и троллей речь не идет). А из этого теста видно что ява быстрее, но больше хавает память, это может объясняться методологиями разработки стандартных библиотек, о чем я и писал выше.

wfrr ★★☆
() автор топика
Ответ на: комментарий от wfrr

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

спасибо К.О., совершенно случайно эта тема уже утром подымалась на ЛОР, но видно ты ее просто не видел

lester ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.