Производительность Java: приватная статическая финальная строка против локальной строки?номер объекта, созданного в пространстве кучи - PullRequest
0 голосов
/ 18 декабря 2018

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

МОЙ ВОПРОС: когда я вызываю badMethod ()собирается ли он создавать новый объект String в пространстве кучи каждый раз, когда я вызываю этот метод?Поэтому, если я вызываю мой метод 1_200_000 раз, собирается ли он создать строковый объект 1_200_000 в пространстве кучи?

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

Спасибо

Я много читал об этом в Google, но не нашел ответа с аргументом или доказательством.Пожалуйста, также, если вы знаете, как я могу проверить это, благодаря акции.

public class Main {

    private static final String HELLO = "hello";
    private static final String WORLD = "world";

    public static void main(String[] args) {

        for (int i = 0; i < 1_200_000; i++) {
           goodMethod();
           badMethod();
        }

    }

    private static void goodMethod(){
        System.out.println(HELLO);
        System.out.println(WORLD);
    }

    private static void badMethod(){
        System.out.println("hello");
        System.out.println("world");
    }

}


// an other example 
Map<String, Object> map = new HashMap<>();
map.put("myKey", xxx.getYYY());
// somewhere else 
map.put("myKey", zzz.getYYY());

// instead of : 
private static final String MY_KEY = "myKey"
map.put(MY_KEY, xxx.getYYY());
map.put(MY_KEY, zzz.getYYY());

РЕДАКТИРОВАТЬ: я не спрашиваю о конкатенации, я удалил конкатенацию из примера кода

Ответы [ 7 ]

0 голосов
/ 18 декабря 2018

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

Я бы сказал, что первый пример менее читабелен и менее удобен в обслуживании, но он одинаков во время выполнения, и не быстрее и не медленнее.

System.out.println("hello");

Вы точно знаете, что он будет печатать, не оглядываясь на класс.

В первом случае вы действительно не знаете, что он будет печатать, если не проверите.

private static final String HELLO = "hello";
private static final String HELLO = "Hello";
private static final String HELLO = "HELLO";
private static final String HELLO = "G'Day";

то есть он менее читабелен, потому что вам нужно изучить два разных места в классе, чтобы узнать, что делает строка кода.

ПРИМЕЧАНИЯ:

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

другой пример

Map<String, Object> map = new HashMap<>();
map.put("myKey", xxx.getYYY());
// somewhere else 
map.put("myKey", zzz.getYYY());

// instead of : 
private static final String MY_KEY = "myKey"
map.put(MY_KEY, xxx.getYYY());
map.put(MY_KEY, zzz.getYYY());

Ни один из примеров не создаетСтроки, но каждый put создает новый объект Map.Entry.

0 голосов
/ 19 декабря 2018

Для примера кода

public class Main {
    private static final String HELLO = "hello";
    private static final String WORLD = "world";

    private static void goodMethod(){
        System.out.println(HELLO);
        System.out.println(WORLD);
    }

    private static void badMethod(){
        System.out.println("hello");
        System.out.println("world");
    }
}

Нет никакой разницы между goodMethod() и badMethod().

Ключевым моментом является то, что не только "hello" и "world" являются константами времени компиляции, но HELLO и WORLD также.

Как Спецификация языка Java® выражает это:

A константа - это final переменная примитивного типа или типа String, которая инициализируется константным выражением ( §15.28 ).Является ли переменная постоянной или нет, может иметь значение в отношении инициализации класса ( §12.4.1 ), двоичной совместимости ( §13.1 ), достижимости ( §14.21) и определенное назначение ( §16.1.1 ).

И в §13.1 :

Ссылка на поле, являющееся постоянной переменной ( §4.12.4 ), должна быть разрешена во время компиляции до значения V, обозначаемого инициализатором постоянной переменной.

Если такое полестатический, тогда в коде двоичного файла не должно быть ссылки на поле, включая класс или интерфейс, объявивший поле.

Другими словами, поле ссылается на HELLO иWORLD разрешается во время компиляции и заменяется их постоянными значениями, как если бы вы записали эти значения в первую очередь.

Вы можете убедиться в этом, посмотрев на байт-код, например, javap -p -c Main:

Compiled from "Main.java"
public class Main {
  private static final java.lang.String HELLO;

  private static final java.lang.String WORLD;

  public Main();
    Code:
       0: aload_0
       1: invokespecial #1     // Method java/lang/Object."<init>":()V
       4: return

  private static void goodMethod();
    Code:
       0: getstatic     #2     // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4     // String hello
       5: invokevirtual #5     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #2     // Field java/lang/System.out:Ljava/io/PrintStream;
      11: ldc           #6     // String world
      13: invokevirtual #5     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      16: return

  private static void badMethod();
    Code:
       0: getstatic     #2     // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4     // String hello
       5: invokevirtual #5     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #2     // Field java/lang/System.out:Ljava/io/PrintStream;
      11: ldc           #6     // String world
      13: invokevirtual #5     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      16: return
}

Вам не нужно нижнее бельево всех подробностях байт-код, чтобы увидеть, что скомпилированный код обоих методов, goodMethod() и badMethod(), идентичен.Конечно, идентичный код не может иметь различий в производительности, связанных с тем, как он был создан.

То же самое относится и ко второму примеру, нет разницы между использованием строкового литерала или постоянной переменной.

Что касается стиля кодирования, я согласен с Ответом Питера Лоури .Использование постоянной переменной не улучшает код, если его имя не дает дополнительного значения.Без такого значения фактическое значение говорит больше, чем имя переменной.Смотри также этот ответ .

0 голосов
/ 18 декабря 2018

Реализация goodMethod относится к пулу строк.Реализация badMethod ссылается на объект.когда мы передаем строку напрямую как println, она называется объектом.

0 голосов
/ 18 декабря 2018

Оба примера равны и работают с тремя объектами в памяти ("привет", "мир", "мир приветствия").Объединение двух литералов является выражением времени компиляции.Вы можете проверить это просто:

// test one object used for constants
String stringOne = "constant";
String stringTwo = "constant";
assertThat(stringOne)
  .isSameAs(stringTwo);

// test one object is used for string literals concatenation
stringOne = "constant2";
stringTwo = "constant" + "2";
assertThat(stringOne)
  .isSameAs(stringTwo);

Аналогичный вопрос о загрязнении строки

0 голосов
/ 18 декабря 2018

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

при работе со строками, например:

String TEST = "test";
test += "copy paste";

проверка строкине изменяется в памяти, просто новая строка создается в пуле строк (или уже существует), и переменная TEST отныне будет указывать на новую (или из кучи) строку.Старый - «тест» все еще будет там, пока GC не сделает свою работу.

попробуйте эту статью - https://www.baeldung.com/java-string-pool это довольно простая концепция

0 голосов
/ 18 декабря 2018

На самом деле я думаю, что JVM не так глупо.На этом этапе он может проверить, что у вас уже есть "hello" как строка, и не создавать ее снова.Просто используйте это снова.Проблема в том, что вы создаете уникальные строки или строки с помощью конструктора: new String("hello").Затем каждый раз, когда берется новый пробел.

PS вы также создаете уникальную строку, объединяя ее с +.Теперь в вашем примере System.out.println("hello" + "world"); попытается создать, если еще не существует "hello", "world" и "helloworld" строк.

0 голосов
/ 18 декабря 2018

Конкатенация Постоянная времени компиляции String s приводит к Постоянной времени компиляции String.Так что это не имеет значения - конкатенация не происходит во время выполнения .

Если бы String s не было константами времени компиляции , тогда вам всегда нужноновый объект (кроме, возможно, в тривиальных случаях).

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