Не будет ли закрытие стриптиза причиной утечки?

Я понимаю, что в java GC в конечном итоге очистит объекты, но я спрашиваю, не является ли плохая практика не закрывать строковый писатель, в настоящее время я делаю это:

 private static String processTemplate(final Template template, final Map root) {
        StringWriter writer = new StringWriter();
        try {
            template.process(root, writer);
        } catch (TemplateException e) {
            logger.error(e.getMessage());
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
        finally {

        }

        return writer.toString();
    }

Должен ли я закрывать запись и создавать новую строку следующим образом:

String result = "";

...

finally {
  result = writer.toString();
  writer.close();
}

Это лучше сделать?

Ответы

Ответ 1

javadoc довольно явный:

Закрытие StringWriter не влияет.

И быстрый взгляд на код подтверждает это:

public void close() throws IOException {
}

Ответ 2

Он не содержит никакого ресурса без памяти. Это будет сбор мусора, как и все остальное. Вероятность close() существует только потому, что другие объекты-хранители хранят ресурсы, которые необходимо очистить, а close() необходим для насыщения интерфейса.

Ответ 3

В конце метода нет ссылки на writer, поэтому он будет освобожден GC.

Ответ 4

Нет, не закрытие StringWriter не приведет к утечке: как отмечено, StringWriter#close() - это nop, а у писателя есть только память, а не внешние ресурсы, поэтому они будут собраны, когда сборщик будет собран. (Явно, он содержит ссылки на объекты в частных полях, которые не выходят из объекта, а конкретно StringBuffer, поэтому никаких внешних ссылок.)

Кроме того, вы обычно не должны закрывать StringWriter, потому что он добавляет шаблон к вашему коду, скрывая основную логику, как мы увидим. Однако, чтобы успокоить читателей, что вы осторожны и делаете это намеренно, я бы рекомендовал прокомментировать этот факт:

// Don't need to close StringWriter, since no external resource.
Writer writer = new StringWriter();
// Do something with writer.

Если вы хотите закрыть автора, наиболее элегантным является использование try-with-resources, который автоматически вызовет close() когда вы выходите из тела блока try:

try (Writer writer = new StringWriter()) {
    // Do something with writer.
    return writer.toString();
}

Однако, поскольку Writer # close() throws IOException, вашему методу теперь нужно также бросить IOException, хотя это никогда происходит, или вам нужно поймать его, чтобы доказать компилятору, что он обрабатывается. Это очень важно:

Writer writer = new StringWriter();
try {
    // Do something with writer, which may or may not throw IOException.
    return writer.toString();
} finally {
    try {
        writer.close();
    } catch (IOException e) {
        throw new AssertionError("StringWriter#close() should not throw IOException", e);
    }
}

Этот уровень шаблона необходим, потому что вы не можете просто поместить catch в общий блок try, так как иначе вы можете случайно усвоить IOException, выброшенную телом вашего кода. Даже если в настоящее время их нет, некоторые могут быть добавлены в будущем, и вы должны быть предупреждены об этом компилятором. AssertionError документирует текущее поведение StringWriter#close(), которое может потенциально измениться в будущей версии, хотя это крайне маловероятно; он также маскирует любое исключение, которое может возникнуть в теле try (опять же, это никогда не должно происходить на практике). Это слишком много шаблонов и сложности, и вам явно будет лучше не пропускать close() и комментировать почему.

Тонкая точка состоит в том, что не только Writer#close() бросает IOException, но также StringWriter#close(), поэтому вы можете Исключить исключение, сделав переменную a StringWriter вместо Writer. Это отличается от StringReader, который переопределяет метод close() и указывает, что он не генерирует исключения! См. мой ответ на Должен ли я закрыть StringReader?. Это может выглядеть неправильно - почему у вас есть метод, который ничего не делает, но может вызывать исключение? - но, по-видимому, для прямой совместимости, чтобы оставить возможность бросать IOException в ближайшем будущем, поскольку это проблема для писателей в целом. (Это также может быть просто ошибкой.)

Подводя итог: отлично, чтобы не закрыть StringWriter, но причина не делать обычную правильную вещь, а именно try-with-resources, заключается только в том, что close() заявляет, что она выдает исключение, Фактически бросать на практике, и для этого достаточно много шаблонов. В любом другом случае лучше просто использовать условно правильный шаблон управления ресурсами и предотвратить проблемы и поцарапать голову.