Scala:如何进行字符串连接以避免 GC 开销问题

Posted

技术标签:

【中文标题】Scala:如何进行字符串连接以避免 GC 开销问题【英文标题】:Scala: how to do String concatenation to avoid GC Overhead issue 【发布时间】:2018-03-21 20:24:20 【问题描述】:

我有一个应用程序,它需要一些非常大的分隔文件(约 10 到 15 M 记录)并在进行一些预处理后将其摄取到 Kafka 中。作为此预处理的一部分,我们将分隔记录转换为 json 并将元数据添加到该 json 消息(文件名、行号)。我们使用 Json4s Native 序列化器来做这件事,如下所示:

import org.json4s.native.Serialization._ 
//some more code and below is the final output.
write(Map(
      "schema" -> schemaName,
      "data" -> List(resultMap),
      "flag" -> "I")
    )

将消息转换为 Json 后,我们会添加消息元数据,例如:

def addMetadata(msg: String, metadata: MessageMetadata): String = 
val meta = write(asJsonObject(metadata))
val strippedMeta = meta.substring(1, meta.length -1)
val strippedMessage = msg.substring(1, msg.lastIndexOf(""))
"" + strippedMessage + "," + strippedMeta + ""
msg

最后的消息看起来像这样:

"schema":"SchemaName"
  "data": [
 ],
  "flag": "I",
 "metadata":"srcType":"file","fileName":"file","line":1021

现在这两种方法都在泄漏一些内存并抛出错误。该应用程序具有每分钟处理 300k 条消息的能力,但在大约 4-5 分钟后它会变慢并最终死亡。我知道字符串连接会产生很多垃圾对象,想知道最好的方法是什么?

java.lang.OutOfMemoryError: 超出 GC 开销限制

【问题讨论】:

即使字符串连接在这里和那里分配了一些不必要的字符缓冲区,它也不应该在一天结束时泄漏任何东西。如果您的应用程序流血而死,原因可能在其他地方。你可能会觉得this 至少很有趣... 【参考方案1】:

当产生大量这样的短消息时,就会产生大量微小的短命对象。 GC 可以非常有效地处理这种微小的短期对象 - 它不太可能导致任何严重的问题。

消息

java.lang.OutOfMemoryError: 超出 GC 开销限制

表示 GC 非常努力地工作,但没有任何成功。微小的短命物体不会发生这种情况。最有可能的是,你有一个大的内存泄漏,几分钟后会占用你所有的内存。然后 GC 必须失败,因为没有什么可回收的。

不要浪费时间优化可能无害的东西。使用一些工具来查找泄漏。

【讨论】:

我找到了导致内存泄漏的对象,我使用 TrieMap 作为一些数据的缓存,一旦容量增加超过 3M,它就会爆炸。【参考方案2】:

尽量使用Stringbuilder,可以避免创建不必要的对象。

Is string concatenation in scala as costly as it is in Java?

【讨论】:

@Explorer,我不熟悉 Scala,但在 Java 中字符串连接成本很高。大多数语言都有相同的行为。问题是 += 附加到字符串会重建一个新字符串,因此它的成本与字符串的长度(两者的总和)成线性关系。 ***.com/questions/1532461/…

以上是关于Scala:如何进行字符串连接以避免 GC 开销问题的主要内容,如果未能解决你的问题,请参考以下文章

Scala控制台:OutOfMemoryError:超出GC开销限制

带有连接的 Sparkjob 中超出了 GC 开销限制

如何使用 maven jvmArg 解决“超出 GC 开销限制”?

将大量数据写入 excel:超出 GC 开销限制

GC垃圾回收机制

jvm学习笔记二(减少GC开销的建议)