StringBuilder - сброс или создание нового

У меня есть условие, что StringBuilder сохраняет строки, соответствующие шаблону из большого плоского файла (100 'с МБ). Однако после достижения условия я записываю содержимое переменной StringBuilder в текстовый файл.

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

stringBuilder.delete(0,stringBuilder.length())

ИЛИ ЖЕ

stringBuilder=new StringBuilder();

Пожалуйста, предложите, что, по вашему мнению, лучше для проблем с производительностью и OOM.

 Dukeling12 сент. 2013 г., 16:30
@SotiriosDelimanolis Массив-копия для длины 0 для этого примера.
 Sanjay T. Sharma12 сент. 2013 г., 16:21
Вы должны провести сравнительный анализ на основе вашего рабочего процесса приложения и выбрать то, что работает лучше для вас.
 Sotirios Delimanolis12 сент. 2013 г., 16:20
Для справки,delete() ЗвонокSystem.arraycopy
 Luiggi Mendoza12 сент. 2013 г., 17:13
Я бы профилировал оба варианта, чтобы измерить производительность как памяти, так и использования процессора, и получить окончательный ответ для конкретного случая. Тем не менее, ИМО лучшим вариантом будетstringBuilder=new StringBuilder(); и пусть GC сделает свою работу.

Ответы на вопрос(8)

Imho, я бы предложил использовать новое: I '

stringBuilder = new StringBuilder();

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

В худшем случае, возможно, вы теряете некоторую эффективность, и gc получает тренировку, но вы исключаете возможность OOM.

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

В идеале мы должны использоватьnew StringBuilder() Копаем немного в классе StringBuilder изgrepcode Я узнаю следующее.

Создание нового объекта:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

new StringBuilder () создает новый объект с массивом символов начальной емкости. Накладные расходы здесь: GC будет вызываться для очистки старого объекта.

Используя delete:

public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

Используя Length и TrimToSize:

public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }

Будет вызывать copyOf из массива класса

public static char [] copyOf (char [] original, int newLength) {char [] copy = new char [newLength]; System.arraycopy (original, 0, copy, 0, Math.min (original.length, newLength)); возвратная копия; }

Теперь это также будет называтьSystem.arraycopy который является родным методом. Теперь, если вы видите в copyOf, мы снова создаем новый charArray длиной 0, и когдамы попытаемся снова добавить к нему данные, это вызовет расширение посколькутекущая длина будет 0, Поэтому я думаю, что лучше вызывать новый StringBuilder ()

Вы можете увидеть выше код наgrepcode

PS: @ user3241961 это запись в случае, если вы используете ссылку на этот объект, тогда new потребуется установить его снова

Решение Вопроса

Я думаюStringBuilder#delete(start, end) Все еще дорогой вызов, вы должны сделать:

stringBuilder.setLength(0);

сбросить его.

ОБНОВИТЬ: Посмотрев наисходный кодStringBuilder Похоже на тоsetLength(int) оставляет старый буфер нетронутым и лучше вызвать:StringBuilder#trimToSize() после вышеупомянутого звонка.attempts to reduce storage used for the character sequence

Так что-то вроде этого будет более эффективным:

stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer
 anubhava12 сент. 2013 г., 17:20
Да, это нужно расширить, но не уверен, почему это опасно. Вместо того, чтобы сохранять огромный буфер IMO, лучше расширять его по требованию.
 Sotirios Delimanolis12 сент. 2013 г., 16:27
Вы можете потерять память, используя эту технику. Если вы создаете все больше и больше строк, то меньшие, лежащие в основеchar[] станет больше, даже если вы неЯ использую все это.
 Sotirios Delimanolis13 сент. 2013 г., 00:27
Я нене знаю, почему ты получил отрицательный голос. Я с тобой согласен.
 Polaris87812 сент. 2013 г., 16:33
@SotiriosDelimanolis может быть прав, посмотрите на источник для:setLengthgrepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/...  В принципе,setLength просто обнуляет старые значения и нена самом деле уменьшить основной массив.
 anubhava12 сент. 2013 г., 17:05
@ SotiriosDelimanolis: Да, я неправильно понялif/else и ясно, что остальная часть будет выполнена. Я думаюsetLength(0) с последующимtrimToSize() было бы лучше для очистки старого буфера.
 Sotirios Delimanolis12 сент. 2013 г., 17:12
@anubhavatrimToSize() тоже опасно, потому что это создастchar[0] который должен быть расширен в первую очередь.append()
 Sotirios Delimanolis12 сент. 2013 г., 16:37
@anubhava I 'Я не говорю о производительности.setLength() Метод просто устанавливаетcount поле на новую длину. Так что если вашchar[] имеет размер 10_000_000, выиспользовать все это пространство даром.
 Davos09 авг. 2017 г., 09:03
@ SotiriosDelimanolis Я знаю, что это древний, но является ли большой неиспользуемый базовый массив, где длина была установлена равной нулю, тем же, что утечка памяти? Я имею в виду, он будет использовать память, но выигралэто будет мусор в конце концов, когда stringBuilder выходит из области видимости? Альтернативно, перераспределение переменной в новый stringBuilder может создать много освобожденного мусора для сбора. Что хуже? Я думаю, это зависит от контекста приложения.
 Sotirios Delimanolis12 сент. 2013 г., 16:47
@anubhava Скажи, что у тебя былcount 100 для предыдущего использования, возможноchar[100] сопровождать это. призваниеsetLength(0), где100 > 0т.е. здесь нет\0, это происходит только тогда, когда емкостьувеличивается, Вместоelse выполняется иcount установлен в0, Так что вашиchar[] ссылка непереназначается, и вы остаетесь сchar[100] который вы не могли бы полностью использовать. Даже если он установил все индексы\0 Вы все равно могли бы тратить их впустую.
 anubhava12 сент. 2013 г., 16:42
@ SotiriosDelimanolis: ЭтоСуть в том, как вы можете сказать, что мыдо сих пор использую это10000000 char array послеsetLength(0)? Кстати, глядя на исходный код, он на самом деле устанавливает все предыдущиеchar[] буфер для\0 индивидуально. Также смотрите этот ответ для более подробной информации и просмотрите некоторые ответы / обсуждения там:stackoverflow.com/a/5193094/548225

Одно фундаментальное отличие - sb.delete сохраняет ссылку, а конструктор теряет ее.

Если ваш SB является аргументом метода и должен использоваться для передачи содержимого обратно вызывающей стороне, вы должны использовать sb.delete. Звонящий хранит исходную ссылку.

Если вы находитесь в замкнутом цикле и после записи данных в файл продолжите работу в этом цикле, вам определенно следует повторно использовать StringBuilder. Там'нет причин не делать этоголучше, чем сбивать GC. Если бы вы писали это на C или C ++, вы бы повторно использовали буфер.

Кроме того, хотя истина в том, что метод delete (...) вызывает System.arraycopy, количество скопированных байтов равно 0, поэтомунезначительно.

Ах, кто-то еще упомянул меня, что существует метод setLength (...), который является самым быстрым способом повторного использования буфера.

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

Более быстрый способ:

stringBuilder.setLength(0);

Я хотел бы использовать:

 stringBuilder = new StringBuilder();

потому что, если вы заполните его большим количеством данных, вызовstringBuilder.setLength(0); победил'• Нераспределите резервный массив, чтобы вы могли видеть, что использование памяти остается высоким без необходимости.

Кроме того, этопросто легче читать и понимать.

 Dukeling12 сент. 2013 г., 16:33
Ну, если тыЕсли вскоре снова использовать все / большинство из них, можно утверждать, что освобождать и перераспределять его не нужно.

Ну тамs большая разница между двумя. Первая сохраняет любую емкость, которая была у вас до удаления символов (т.е.stringBuilder.capacity()) тогда как второй создает новыйStringBuilder с емкостью по умолчанию, 16. Конечно, вы могли бы просто передатьstringBuilder.capacity() в качестве аргумента для конструктора, но это 'Тем не менее, важно понимать различие здесь.

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

Ваш ответ на вопрос