Управление строками и памятью - PullRequest
5 голосов
/ 08 августа 2011

Мне трудно понять, что происходит со строками и управлением памятью в Android (Java).

Чтобы упростить, взгляните на этот простой кусок кода:

public void createArray(){
        String s = "";
        String foo = "foo";
        String lorem = "Lorem ipsum ad his scripta blandit partiendo, eum 
                 fastidii accumsan euripidis in, eum liber hendrerit an.";
        ArrayList<Item> array = new ArrayList<Item>();

        for( int i=0; i<20000; i++ ){
            s = foo.replace("foo", lorem);
            array.add( new Item(s) );
        }
        System.gc();
    }

private class Item{
    String name;

    public Item(String name){
        this.name = name;
    }
}

Если выполнено, createArray выделит более 5 МБ в памяти. Даже с инструкцией System.gc() память не будет освобождена после цикла.

Теперь, если мы заменим s = foo.replace("foo", lorem); на s = lorem;, выделенная память увеличится только на 0,5 МБ.

Мне нужно понять, что происходит, чтобы улучшить производительность моего приложения.

Кто-нибудь может объяснить, как я должен заменить строки в таком случае, как это? И почему System.gc() не освобождает память?

Спасибо.

ОБНОВЛЕНИЕ :

Спасибо за ответы, теперь я понимаю, что System.gc() это только подсказка.

Для уточнения другого вопроса (важного):

Как я могу динамически генерировать 20000 строк ("foo1", "foo2" ... "foo20000"), добавлять их в ArrayList и не исчерпывать память? Если бы эти 20000 строк были статическими, они бы не выделяли более 0,5 МБ в памяти. Кроме того, если s = foo.replace("foo", lorem) создает новую строку, почему моя функция выделяет 5 МБ, что в 10 раз больше памяти? Не должно быть около 1 МБ?

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

Ответы [ 4 ]

5 голосов
/ 08 августа 2011

replace генерирует новый объект String каждый раз, когда он вызывается, так как строки в Java являются неизменяемыми, и поэтому модификации не могут быть сделаны «на месте».Кроме того, вы добавляете String к ArrayList, который будет содержать ссылку на новый объект, предотвращая его сбор.

2 голосов
/ 08 августа 2011

System.gc () - это подсказка сборщику мусора для повторного запуска, когда у него есть время.

Тем не менее, сборщик мусора собирает только объекты, на которые нет ссылок, и все созданные вами строки все еще удерживаются объектом array. Вполне возможно, что вы можете добавить строки после System.gc()

for (String item : array) {
   System.out.println(item);
}

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

В случае, если у вас есть реальная необходимость повторно использовать строку, вы можете обменять циклы ЦП на, возможно, меньший объем памяти, используя методы String intern (которые будут сканировать выделенные строки на наличие дубликатов и возвращать ссылку на одну сохраненную строку, если найдено.

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

В конце концов, при такой экзотической оптимизации времени компиляции невозможно точно выполнить отражение, пошаговое выполнение кода, привязку компиляции к исходному коду и различные типы отражения. Тем не менее, Java хорошо оптимизирует себя, учитывая, сколько функций вы получаете из коробки.

2 голосов
/ 08 августа 2011

Поскольку String являются неизменными, вызов foo.replace("foo", lorem) будет каждый раз создавать новый String.В примере, где вы просто устанавливаете s = lorem, новый String не создается.

Кроме того, System.gc () является просто рекомендацией для виртуальной машины и никоим образом не гарантирует сборку мусора.,Вы ничего не можете сделать, чтобы форсировать сборку мусора.(кроме использования всей доступной памяти)

1 голос
/ 08 августа 2011

Имейте в виду, что System.gc() не заставляет сборщик мусора работать.Это просто намек на то, что вы хотели бы, чтобы он работал в какой-то момент в будущем.

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