【问题标题】:Optimising writing StringBuilder's content to ServletResponse优化将 StringBuilder 的内容写入 ServletResponse
【发布时间】:2012-03-08 13:13:28
【问题描述】:

我想获得一些关于优化我为将 StringBuilder 的全部内容写入 ServletResponse 而创建的方法的方法。

我这样做是为了避免在将其传递给 out.write() 方法之前一次性创建巨大的字符串。在我的情况下,StringBuilder 的内容长度在某些情况下会达到几百万个字符。

public static void writeResponse(ServletResponse response, StringBuilder sb) throws IOException {
    try (PrintWriter out = response.getWriter()) {
        int length = sb.length();
        //to avoid creation of gigantic strings we are writing substrings from the sb 
        int bufferSize = (response.getBufferSize() != 0? response.getBufferSize():10000);
        log.log(Level.INFO, "READY TO SEND To CLIENT, length of responseSB={0}", length);
        if (length <= bufferSize) {
            out.write(sb.toString());
        } else {
            int noWrites = length / bufferSize;
            for (int i = 0; i < noWrites; i++) {
                out.write(sb.substring(i * bufferSize, (i + 1) * bufferSize));
                log.log(Level.INFO, "SENDING To CLIENT, write no={0} of {1}", new Object[]{(i + 1), noWrites});
            }
            int rest = length % bufferSize;
            if (rest != 0) {
                out.write(sb.substring(length - rest, length));
            }
        }
    }
}

我希望它写一条(不是分块的)消息。因此,我想知道相对于字符数(或字符串的长度)建立响应缓冲区大小的准确程度如何?

目前,我正在获取当前缓冲区大小并使用它,就好像它表达了它可以容纳的多个字符一样,如何正确评估缓冲区大小?另外我没有包括标题大小,我该如何实现它

我想将其性能优化到最大(因此它工作得最快),非常感谢任何建议。或者也许有更好的方法将巨大的 StringBuilder 内容写入 ServletResponse?

【问题讨论】:

  • 我认为你不应该担心缓冲区的大小。让容器来做。
  • @mKorbel 也很高兴见到你。我知道,自从我们上次见面以来,发生了很多变化。你已经获得了 100% 以上的代表。我的祝贺。做得好。还是大部分都聚集在swing SO 的一部分:)

标签: java jakarta-ee response stringbuilder servlet-3.0


【解决方案1】:

最快的方法是:

out.write(sb.toString());

如果您想节省内存,请将StringBuilder 替换为PrintWriter 并传递response.getWriter()

任何关于缓冲区大小的优化只会让事情变慢。如果没有优化,代价大概是:StringBuilder.toString() + out.write() 将长字符串传递给容器进行分块/发送。

通过您的优化,它看起来像这样:StringBuilder.toString() + substring() + out.write() + 将子字符串复制到发送缓冲区 + 多次调用容器以发送片段。

如果您摆脱了构建器,对容器的调用次数将保持不变(out.write() 使用内部缓冲区),但您不会浪费内存来保存数据。

如果你想保留StringBuilder,那么找出页面有多大,并创建一个非默认大小的StringBuilder,这样它就不必一直扩展它的内部缓冲区。

【讨论】:

  • 感谢您的回复 (+1)。我知道out.write(sb.toString()); 是最快的,正如你所说,它是记忆杀手。你能解释一下你的意思是什么:If you want to save on memory, replace StringBuilder with PrintWriter and pass response.getWriter() around.?您的意思是为了节省内存我应该直接写入响应吗?
  • 是的。响应将收集输出,直到缓冲区已满。这意味着:您不必保留数据,浏览器将在您从 servlet 返回之前开始接收数据。而且由于 TCP/IP 不能在每个数据包中发送超过 ~1500/8000 字节的数据,因此无论您是否愿意,数据都会被切碎。
  • 注意:总是传递 writer,而不是 response 对象。这样,您可以轻松地在单元测试中测试代码。
  • +1 用于传递作者。从根本上解决问题。尽管这几乎肯定会发送分块响应。
  • 还要注意WriterStringBuilder都实现了Appendable接口,所以你可以传递一个appendable来进一步减少耦合。
猜你喜欢
  • 2011-01-15
  • 2022-07-23
  • 1970-01-01
  • 2017-07-18
  • 2010-10-09
  • 1970-01-01
  • 2019-09-06
  • 2017-07-01
  • 1970-01-01
相关资源
最近更新 更多