String s = "";
for(i=0;i<....){
s = some Assignment;
}
или же
for(i=0;i<..){
String s = some Assignment;
}
Мне больше не нужно использовать 's' вне цикла. Первый вариант, возможно, лучше, поскольку новая строка не инициализируется каждый раз. Однако второй вариант приведет к тому, что область действия переменной будет ограничена самим циклом.
Обновлено: В ответ на ответ Милхауса. Было бы бессмысленно назначать String константе внутри цикла, не так ли? Нет, здесь «какое-то присвоение» означает изменяющееся значение, полученное из списка, по которому выполняется итерация.
Кроме того, вопрос не в том, что меня беспокоит управление памятью. Просто хочу знать, что лучше.
Еще один комментарий: не забывайте, что если вы не собираетесь изменять значение s, вы должен объявляете его final. Многие программисты Java часто забывают об этом.




В теория объявлять строку внутри цикла - пустая трата ресурсов. Однако в упражняться оба представленных вами фрагмента будут компилироваться до одного и того же кода (объявление вне цикла).
Итак, если ваш компилятор выполняет какую-либо оптимизацию, разницы нет.
Ссылка просто помещается внутри фрейма стека для вызова этого метода, верно?
Я думаю, вы неверно истолковали то, что написала 1800 ИНФОРМАЦИЯ. Неизменяемость строк Java здесь не имеет значения: «некоторое присвоение» будет создавать новую строку каждый раз, независимо от того, является ли строка неизменной.
Обновили Вопрос. Назначение жестко закодированной строки было бы нелогичным.
Что это за теория, говорящая вам, что переменная объявление тратит ресурсы впустую?
jrudolph: Создание новой пустой строки создает новый объект, собирающий мусор, если я правильно помню свою Java.
В общем, я бы выбрал второй, потому что область действия переменной s ограничена циклом. Выгоды:
Чтобы добавить немного к @ Ответ Эстебана Арайи, они оба потребуют создания новой строки каждый раз в цикле (как возвращаемое значение выражения some Assignment). В любом случае эти строки необходимо собрать с помощью сборщика мусора.
Не обязательно. Мы не знаем, что назначается «s» внутри цикла. Возможно, это строковая константа, которая выделяется в пространстве PermGen и никогда не будет собираться сборщиком мусора. Все, что мы знаем, это то, что что бы это ни было, оно одинаково в обоих случаях, поэтому это не имеет значения.
@erickson: Я согласен с тем, что это может быть строковая константа, но в этом случае я ожидаю, что компилятор, вероятно, будет использовать постоянное распространение, чтобы вывести s из тела цикла. Я предполагал, что это не константа, потому что разумный программист поступил бы так же.
Я не имел в виду одну и ту же строковую константу на каждой итерации. См. Мой комментарий к OP.
Да в таком случае оптимизации не было бы
Мне кажется, что нам нужно более детально проработать проблему.
В
s = some Assignment;
не указано, что это за назначение. Если задание
s = "" + i + "";
затем нужно выделить новое жало.
но если это
s = some Constant;
s будет просто указывать на ячейку памяти констант, и, таким образом, первая версия будет более эффективной с точки зрения памяти.
Кажется, мне немного глупо беспокоиться о большой оптимизации цикла for для интерпретируемого языка IMHO.
Воспользуйтесь вторым вариантом:
for ( ... ) {
String s = ...;
}
Если вы дизассемблируете код, скомпилированный из каждого (с помощью инструмента JDK javap), вы увидите, что цикл компилируется с одинаковыми инструкциями JVM в обоих случаях. Также обратите внимание, что Брайан Р. Бонди «Вариант №3» идентичен Варианту №1. При использовании более узкой области ничего лишнего не добавляется и не удаляется из стека, и в обоих случаях в стеке используются одни и те же данные.
Единственное различие между этими двумя случаями состоит в том, что в первом примере переменная s инициализируется без необходимости. Это отдельная проблема от места объявления переменной. Это добавляет две потраченные впустую инструкции (для загрузки строковой константы и сохранения ее в слоте кадра стека). Хороший инструмент статического анализа предупредит вас о том, что вы никогда не читаете значение, присвоенное s, а хороший JIT-компилятор, вероятно, пропустит его во время выполнения.
Вы можете исправить это, просто используя пустое объявление (например, String s;), но это считается плохой практикой и имеет еще один побочный эффект, обсуждаемый ниже.
Часто фиктивное значение, такое как null, присваивается переменной просто для того, чтобы скрыть ошибку компилятора о том, что переменная читается без инициализации. Эта ошибка может быть воспринята как намек на то, что область видимости переменной слишком велика и что она объявляется до того, как потребуется получить допустимое значение. Пустые объявления заставляют вас рассматривать каждый путь кода; не игнорируйте это ценное предупреждение, присваивая фиктивное значение.
Как уже упоминалось, хотя инструкции JVM одинаковы в обоих случаях, существует тонкий побочный эффект, который позволяет лучше всего на уровне JVM использовать максимально ограниченный объем. Это видно в «таблице локальных переменных» для метода. Подумайте, что произойдет, если у вас есть несколько циклов с объявлением переменных в излишне большой области видимости:
void x(String[] strings, Integer[] integers) {
String s;
for (int i = 0; i < strings.length; ++i) {
s = strings[0];
...
}
Integer n;
for (int i = 0; i < integers.length; ++i) {
n = integers[i];
...
}
}
Переменные s и n могут быть объявлены внутри соответствующих циклов, но поскольку это не так, компилятор использует два «слота» в кадре стека. Если они были объявлены внутри цикла, компилятор может повторно использовать тот же слот, уменьшив размер стека.
Однако большинство из этих вопросов несущественны. Хороший JIT-компилятор увидит, что невозможно прочитать начальное значение, которое вы расточительно присваиваете, и оптимизирует назначение. Сохранение слота здесь или там не приведет к поломке вашего приложения.
Важно сделать ваш код удобочитаемым и простым в обслуживании, и в этом отношении использование ограниченной области явно лучше. Чем меньше объем переменной, тем легче понять, как она используется и какое влияние окажут любые изменения в коде.
Я все еще не полностью уверен, но я удалил свой ответ, потому что он был неправильным, но для справки по этому вопросу. Вот что у меня было: {// Не снимать фигурные скобки String s; for (i = 0; i <....) {s = некоторое присвоение; }}
Если бы я мог, я бы проголосовал за этот комментарий дважды. Я бы также отметил вопрос «ранняя оптимизация».
Какой отличный ответ; Я бы также проголосовал за него несколько раз, если бы мог.
Я раньше не играл с javap, поэтому проверил цикл for, который получает дату и устанавливает String s равным date.toString. Сделанная мною разборка показала, что код другой. Вы можете видеть, что s устанавливается в каждом цикле внутреннего. Это что-то, что может исправить jit-компилятор?
@ Филип Т. - Я не уверен, что понимаю, что вы описываете. Если вы имеете в виду, что одно и то же значение присваивалось String на каждой итерации цикла, и спрашиваете, можно ли «поднять» это вычисление, да, это возможно, но это будет зависеть от JVM.
Мой вопрос касался раздела «Объем не влияет на производительность». Поскольку мне было любопытно, я посмотрел на вывод javap класса, реализующего пример с переменной, определенной внутри и вне цикла. Javap показывает, что код другой, с ldc и astore внутри цикла для внутренней инициализированной версии, и этот код находится вне цикла для внешней инициализированной версии. Я пытаюсь выяснить, не упускаю ли я чего-то, поскольку общее мнение, похоже, заключается в том, что код не должен отличаться. Я проверил классы из Eclipse и Netbeans.
Вы говорите о начальном бесполезном назначении "" на s в первом примере в исходной публикации? Значение, которое никогда не читается, прежде чем оно будет перезаписано на первой итерации цикла? Об этом я и говорю в разделе «Избегайте преждевременной инициализации»; инструкции ldc и astore тратятся впустую, потому что это значение никогда не может быть прочитано до переназначения переменной. Код внутри у цикла такой же. Инициализация (присвоение) переменной - это отдельный вопрос от области действия переменной.
Если вы хотите ускорить циклы, я предпочитаю объявлять переменную max рядом со счетчиком, чтобы не требовалось повторных поисков условия:
вместо
for (int i = 0; i < array.length; i++) {
Object next = array[i];
}
я предпочитаю
for (int i = 0, max = array.lenth; i < max; i++) {
Object next = array[i];
}
Любые другие вещи, которые следует учитывать, уже упоминались, поэтому только мои два цента (см. Сообщение Ericksons)
Greetz, GHad
Я знаю, что это старый вопрос, но я подумал, что добавлю немного, связанное с немного.
При просмотре исходного кода Java я заметил, что некоторые методы, такие как String.contentEquals (дублированные ниже), создают избыточные локальные переменные, которые являются просто копиями переменных класса. Я считаю, что где-то был комментарий, подразумевающий, что доступ к локальным переменным происходит быстрее, чем доступ к переменным класса.
В этом случае «v1» и «v2» кажутся ненужными и могут быть исключены для упрощения кода, но были добавлены для повышения производительности.
public boolean contentEquals(StringBuffer sb) {
synchronized(sb) {
if (count != sb.length())
return false;
char v1[] = value;
char v2[] = sb.getValue();
int i = offset;
int j = 0;
int n = count;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
}
return true;
}
Вероятно, это было более полезно на старых виртуальных машинах, чем сейчас. Мне трудно поверить, что это имеет значение для HotSpot.
Когда я использую несколько потоков (50+), я обнаружил, что это очень эффективный способ решения проблем с призрачными потоками, когда я не могу правильно закрыть процесс .... если я ошибаюсь, дайте мне знать, почему Я не прав:
Process one;
BufferedInputStream two;
try{
one = Runtime.getRuntime().exec(command);
two = new BufferedInputStream(one.getInputStream());
}
}catch(e){
e.printstacktrace
}
finally{
//null to ensure they are erased
one = null;
two = null;
//nudge the gc
System.gc();
}
Нет ничего необычного в итерации по набору строк, заданных как литералы. Например, заголовки столбцов таблицы могут быть жестко заданы как String []. Однако важно то, что в обоих случаях выполняется одно и то же задание, поэтому оно не влияет на ответ.