【问题标题】:When am I doubling my memory usage?我什么时候将内存使用量翻倍?
【发布时间】:2012-05-16 19:51:04
【问题描述】:

我有一个 servlet,用户可以将 XML 文件发布到。

我使用以下方法读取该文件:

String xml = request.getParameter("...");

现在说 xml 文档是 10KB,因为我创建了变量 xml 我现在为该变量使用了 10KB 的内存对吗?

现在我需要解析该 xml(使用 xerces),并在将其传递给 saxparsers 解析方法时将其转换为输入流(http://docs.oracle.com/javase/1.5.0/docs /api/javax/xml/parsers/SAXParser.html)。

那么,如果我将字符串转换为流,我的内存使用量会增加一倍吗?

需要对此进行一些澄清。

如果我将我的进程与 visualvm 或 jconsole 连接,在单步执行代码时,我是否可以查看在调试器中单步执行代码时是否使用了额外的内存?

我想确保我这样做不会效率低下,因为这个端点会受到重创。

【问题讨论】:

  • 如果你想自己检查发生了什么,你正在寻找一个分析器(YourKit 相当不错,虽然不是免费的)
  • 如果它是一个请求参数,你真的没有其他选择。如果将其放在消息正文中,则可以将流直接读入 XML 结构。
  • 使用流读取字符串不会使内存加倍——事实上这是个好主意。

标签: java servlets jvm jetty


【解决方案1】:

10,000 字节的文本通常会变成 20 KB。

当您处理文本时,您通常需要 2-10 倍的内存,因为您将使用该信息执行某些操作,例如创建数据结构。

这意味着您可能需要 200 KB。但是,鉴于在 PC 中这代表 1 美分的价值,我通常不会担心。如果您的设备资源严重受限,我会考虑将处理转移到另一台设备上,例如服务器。

【讨论】:

  • 每秒 1K 的文件会改变什么吗? :)
  • 大部分手机都能轻松搞定。我不希望您遇到低于 10 MB/s 的问题。如果您有大量数据,我不会使用 XML,因为它会花费大量时间将数据转换为二进制。使用二进制格式,您可以处理 100 MB/s 到 1 GB/s。
  • 手机?这是一个运行在服务器上的服务,终端客户端发送xml文件到服务端点,每秒几千个。
  • 每秒几千并不算多,除非文件很大,例如每个 10 KB 以上。
【解决方案2】:

我认为您可能会在实际看到代码运行之前对其进行优化。 JVM 非常良好且快速地恢复未使用的内存。

但是回答你的问题String xml = request.getParameter("..."); 并没有使内存加倍,它只是为指针分配了额外的 4 或 8 个字节(取决于 JVM 是否使用压缩指针)。

解析 xml 是不同的,SAX 解析器非常节省内存,所以它不会使用太多内存,我认为每个 Handler 大约 20 个字节加上你拥有的任何实例变量......显然你有任何额外的对象可能会在处理程序中生成。

因此,您拥有的代码看起来尽可能节省内存(当然,这取决于您的处理程序中的内容)。

除非您正在将该代码嵌入到设备中或每秒运行 10 万次,否则我建议您不要优化任何东西,除非您确定需要优化它。 JVM 有一些疯狂的高级逻辑来优化代码,垃圾收集器可以非常快速地恢复短期对象。

【讨论】:

  • 实际上我必须每秒构建几千个,并且服务器上没有那么多 RAM。在我遇到问题之前,现在学习这个总是很好的。
【解决方案3】:

如果用户可以将大量文件发回您的 servlet,那么最好不要使用 getParameter() 方法并直接处理流 - Apache File Upload Library

这样您就可以在 InputStream 上使用 SAX 解析器(并且在处理之前不需要将整个文本加载到内存中)——就像您使用基于字符串的解决方案一样。

与 String xml = getParameter(...) 解决方案相比,这种方法扩展性很好,每个请求只需要少量内存。

【讨论】:

  • 文件上传通过 http post (upload.thinfile.com/docs/post.php) 进行。将潜在的大参数作为流处理的优势在于它降低了 DoS 攻击的可能性。一个大的请求可能会关闭服务器,因为它必须将字符串的内容放入 VM 空间,如果太大,VM 将创建一个 OutOfMemoryError。如果这是在一个安全的网络上,并且如果您只预计 XML 小于 1 MB,请坚持使用 getParameter() 解决方案。
【解决方案4】:

你会这样编码:

saxParser.parse(new InputSource(new StringReader(xml));

您首先需要在xml 周围创建StringReader。这不会使您的内存使用量翻倍,StringReader 类仅包装 xml 变量并在请求时逐个字符地返回它。

InputSource 更薄 - 它只是简单地包装提供的ReaderInputStream。简而言之:不,您的String 不会被复制,您的实现非常好。

【讨论】:

  • 所以如果我按照我的理解在另一条线上执行它是完全一样的吗?即InputSource is = new InputSource(new StringReader(xml));它的所有引用,所以我假设是这样,但只是检查。
  • @user1361315:是的,这些只是参考,你没有复制任何东西。如果您使用dom 解析,内存使用率会高得多。还有例如从String 中添加或删除一个字符会导致整个字符串被复制。
【解决方案5】:

不,你不会得到字符串的 2 个副本,你的内存会加倍。其他东西可能会使内存翻倍,但字符串本身不会被复制。 是的,您应该连接 visualVm 和 jconsole 以查看内存和线程处理会发生什么。

【讨论】:

    猜你喜欢
    • 2021-11-15
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多