为什么我的 JVM 有这么多旧的字符和字符串?

Why my JVM has so many old char and string?

jmap -histo {pid} 显示以下结果:

 num     #instances         #bytes  class name
----------------------------------------------
   1:       4787259     1007093680  [C
   2:       6049019      191502048  [B
   3:        198580      125701976  [I
   4:       5212228      125093472  java.lang.String

而 运行 jmap -histo:live {pid} 显示以下结果:

 num     #instances         #bytes  class name
----------------------------------------------
   1:        463375      140980752  [C
   2:          9832       63080312  [I
   3:        563438       31161448  [B
   4:        461206       11068944  java.lang.String

可以看到有很多Char和String虽然没有存活,但是依然占据着堆内存。这是正常的还是我需要担心?我希望 JVM 可以清理一些无用的字符串以节省内存,因为我很确定所有这些字符串都是不同的并且不会再次使用并且不需要保留在内存中以供将来重用。

我主要想知道jmap -histo: livejmap -histo的区别,jmap -histo有1000多M的Char,jmap -histo:live只有140M,哪里有其他860M字符?为什么他们不是 GC?这些Chars什么时候会被GC?如何让它们立即 GC 以节省内存?

正如@boneill 指出的那样,在引擎盖下 jmap -histo:live 执行完整的 GC ... 以确定哪些对象是活动的。而 jmap -histo ... 没有。

Why my JVM has so many old char and string?

Is this normal or do I need to concern?

很难说。证据不足

但是,如果没有其他证据,我会说“不相关”。

where are the other 860M Chars?

首先[C并不代表Char。意思是char[].

他们在哪里?好吧,我的阅读是他们无法到达。 (不直播)。他们是垃圾收集的候选人

... and why they aren't be GC?

因为 GC 还没有收集它们。

Java 垃圾回收……很复杂。许多收集器是分代的,这意味着它们将堆分成新对象和旧对象 space。新的space 经常收集。旧的 space 很少收集。因此,如果这些对象中的大多数已被保留到旧 space,它们在变得无法访问后可能会存活相对较长的时间。

这是一种可能的解释。

When will these Chars be GC?

收集旧的space时。应该是吧。

How can I make them immediately GC to save memory?

可以 调用System.gc() 要求JVM 运行 进行(通常)完全垃圾回收。但是...

  1. JVM 可能完全忽略那个请求,
  2. 它可能不会 运行 完整 GC(这可能取决于实现),
  3. 即使收集了对象,您也很可能不会保存任何实际内存

你为什么不节省内存?因为 JVM 挂在空闲 space 上,它又回来了……准备分配新对象。所以内存没有归还给OS供其他应用程序使用。 (堆大小调整有一些复杂的逻辑,但通常 JVM 会不情愿地 归还内存;即仅在多次完整的 GC 周期之后。)

最后,在您的应用程序代码中调用 System.gc() 对性能不利......在大多数情况下。还有其他问答解释了原因。