【问题标题】:Fastest way to release memory of stringbuilder释放stringbuilder内存的最快方法
【发布时间】:2025-11-27 10:00:01
【问题描述】:

stringbuilder释放内存的最快方法是什么

 StringBUilder sb = new StringBuilder();
 sb.apppend(maximum value possible);

下面是我尝试尽快释放内存并使对象符合垃圾回收条件的代码 sn-ps

选项 1:

sb = null;

据我了解,stringbuilder中的所有数据都将被删除,对象一被踢进去就可以进行垃圾回收,但是stringbuilder占用的文本内存将被释放。里面的字符串值也会从堆中删除还是存储在字符串池中?

选项 2:

 sb.setLength(0);

这将重置字符串生成器的长度,但不会被垃圾回收

选项 3:

 sb.delete(0,sb.length());

这将通过删除其中的所有数据来重置字符串生成器,但不会被垃圾回收

选项 4:

 sb.delete(0,sb.length());
 sb = null;

这将重置字符串生成器并使其符合垃圾收集条件?!

【问题讨论】:

  • 您不能保证在任何特定时间点都会释放内存(如果有的话)。这完全取决于垃圾收集器,并且取决于特定的 JVM 实现和配置。
  • 如果释放内存是您首先关心的问题,您可以编写本机代码,将其与 JNI 链接并自己管理内存,但这样做会破坏使用 Java 的大部分目的。就像 GhostCat 所问的那样,为什么?
  • 重新字符串池:StringBuilders never 的内容去那里。 永远不会。只有实际的Strings 会去那里,然后只有如果你在运行时通过调用.intern() 专门把它们放在那里,或者编译器在编译时把它们放在那里。跨度>

标签: java performance garbage-collection


【解决方案1】:

如果您真的希望尽快释放内存,那么您需要查看 GC 选项,而不是代码。

为此目的对 GC 选项的详细分析超出了此处的合理范围 - 但具体而言,可以将新的 Shenandoah GC 调整为在释放内存方面比 G1 或并发标记更 /sweep,所以这可能是您想要使用的。您可以使用-XX:+UseShenandoahGC 指定它。 (在这一点上,它是 OpenJDK 特定的和实验性的,但如果你想要你所追求的行为,它就是你想要的。)

为了加快发布时间,您可能希望为 ShenandoahUncommitDelayShenandoahGuaranteedGCInterval 使用较小的值。此处较小的值(广义而言)将更频繁、更积极地运行 GC,因此使用更多 CPU 周期 - 但它会产生您所追求的效果,与以前的 GC 化身相比,内存将被非常快地释放。

如果您只是想确保内存被释放以便它有资格快速进行垃圾回收,那么您只需要确保所有对 StringBuilder 的引用都设置为 null最快的机会。即使在这种情况下,我还是鼓励您分析并检查您是否需要显式执行此操作 - 现在在许多情况下,JVM 足够聪明,可以将对象标记为符合 GC 条件,即使它们在技术上仍在范围内(只要它可以看到它们在该范围的其余部分不再被引用。)

【讨论】:

  • @GhostCat 是的,OpenJDK。我应该注意到答案不是标准的(和实验性的)。
  • 我们去... ;-)
  • @MichaelBerry 很好的答案!请注意,并非所有 jdk 版本都具有 Shenandoah(您甚至无法启用它们)并且 ShenandoahUncommitDelay 仅释放这段时间为空的区域。
【解决方案2】:

简单:搭配

sb = null;

然后忘记其余的。

为什么?一旦 GC 可以确定一个对象有资格进行垃圾回收,它也知道从该对象引用的所有内容都是合格的。考虑到您无法控制 GC 启动、清理对象和释放内存,因此上述分配确实足够好。

因为它清楚地传达了您的意图,然后帮助 GC 完成其工作。实际上写得好的代码可能甚至不需要那个语句。

现在,也许这实际上还不够好。但接下来我们将在一个非常明确和狭隘的背景下讨论一个真正的性能问题。在这种情况下,您不必依赖道听途说,而是开始衡量。

换句话说:如果您的设置中没有表现出真正的性能问题,并且需要采取措施,那么您可以使用简单、清晰的代码,避免各种“源代码级别”优化。但是,如果您遇到真正的性能问题,那么您应该相应地对待它:确定根本原因,并且可能通过衡量不同选项在现实中的效果。

其他任何东西都带有过早优化的味道!

【讨论】:

    【解决方案3】:

    确保StringBuilder 符合垃圾回收条件的唯一安全方法与所有对象相同:杀死对它的所有引用。这意味着将其显式设置为 null 或让它超出范围。

    除此之外,您无法控制何时释放内存。你能做的最多就是请求垃圾收集,这不是一个好主意(更不用说你只是请求,而不是命令 JVM 进行垃圾收集)。

    除非您已深入研究代码并确信您已完全刷新对象的状态,否则您提出的其他选择不能保证释放所有内存。这就是为什么如果您想确保一切都“干净”,通常最好将整个对象引用清空并重新开始。

    【讨论】:

      【解决方案4】:

      只需让 StringBuilder 对象超出范围(在右花括号处),不要徒劳地用不必要的语句使代码混乱,以使垃圾收集器更快地完成工作。当 StringBuilder 对象超出范围时,它将有资格进行垃圾回收。保持源代码干净。

      【讨论】:

      • 对!即使我也想那样,但老年人几乎不明白:)
      • 听起来你的前辈来自不同的编程语言,有自己的范式。我会轻轻地向他们解释,这真的不是 java 的工作原理。
      【解决方案5】:

      一个可能的解决方案包括 3 个步骤:

      1. 致电sb.setLength(0)。这会将字符串生成器的内部长度设置为 0,这是第 2 步所必需的。
      2. 致电sb.trimToSize()。这将用一个新的修剪数组替换value 数组,在我们的例子中是一个大小为0char[]
      3. sb = null 将删除您对 StringBuilder 的引用。

      如果字符串生成器仅由您使用并且没有传递到其他任何地方,这应该使之前的value 数组和字符串生成器可以用于垃圾收集。

      虽然实际发生的时间仍然取决于 JVM,因此“最快的方式”可能是不可能的,因为它实际上是非常随机的。

      【讨论】:

      • 您也可以调用System.gc();,它会指示JVM 对垃圾收集进行优先级排序...但不能保证它可以工作...而且确实不应该是必要的。为了你的合作​​者的集体理智,请不要这样做。
      • @AhmadQureshi 您删除了对 value-array 内部 StringBuilder 的引用,但它仍然保留在内存中,直到 GC 拾取它。我们只是告诉 JVM 我们不再需要它了
      • 他们应该在垃圾回收后释放它。我相信早点这样做的唯一方法是sun.misc.Unsafe - freeMemory 方法。不过,这确实不是一个好主意...考虑一下为什么您认为需要快速释放内存。
      • @Lino "call sb.setLength(0)。这将用 \0 ("null"-char)" 填充字符串构建器内的值数组——这仅在您调用 setLength 时发生参数大于当前长度。 SetLength(0) 对 OP 所追求的内容没有影响。
      • @Lino 您的编辑没有反映我的意思。即使您当前的字符串有 20 个字符,当您调用 setLength(0) 时,它 除了将 count 设置为 0 之外什么都不做。将未使用的字符设置为 '\0' 是没有意义的;它只会浪费CPU时间。只有当您展开字符串以使其比当前更大时,它才会将 chars 设置为 0。