问题的提出,分析,请参考JNI——小心,内存怪兽出没

(简单的说起来,就是java进程占用了4G内存,但是折腾来折腾去,整个JVM的堆才100M上下,其余的内存凭空消失?刨根问底之后,原来是native heap占用了内存)

看完上面的问题,再来看解决方案:

 

 

 

目前看来,通过调整 JVM 参数来加大 GC 触发的频率是比较现实的一种方式。下面是一些分析过程:

 

一.       判断 Memonry 内存所属的区

通过 jconsole 可以看到 gc 信息,其中新生代的 scavenge 回收比较频繁,如果 Memonry 在这个区应该不存在长期得不到 gc 的情况

由此推测 Memory 应该是在 Tenured/old ,而这个区的内存回收次数恰好非常少。 当然参数优化的目标,就是加大 old 区的 gc 频率。

 

二.       观察 JVM 的堆参数配置

1. 默认配置下,堆的分配起始值为 126M ,最大 1.8G  

 

2. 观察 gc 前后 java 整个堆 的使用情况, gc 前大概占 130M, 远未达到默认设置下的内存容量

 

3. 进一步查看 old 区的内存 ,发现在 gc 前稳定在 90M 。

而默认参数初始分配 85M ,最大 1.3G ,显然很难触发到 GC

 

三.       解决问题

1. 首要的问题就是控制 JVM 对整个 heap 的大小分配:

 -Xmx300M    指定 jvm 的最大 heap 大小 ,

-Xms40M

 

2.

            将默认的 NewRatio=2 更改为更符合业务实际内存使用比例的  -XX:NewRatio=1 

          

 

         3.

启用 jvm 中的 gc 参数

这个参数的大概含义就是,让每次 gc 的时间不超过参数 nnn 。那么 nn 减少的时候,必然会增加到 gc 的次数,来换取每次 gc 的速度。

通过下图的数据可以求出,每次 old 区的 gc 平均耗时 34.7ms , 那么可以   -XX:MaxGCPauseMillis=28 ,也许会增加到 gc 频率。

 

 

4 个标红参数已经添加 ,观察几天得到的结论是,比较好的解决了这个问题。

====================================================================

分割线:下面是调优后的观察:

 

可以看到整个内存呈波动结构,Java的堆从20M--》60M就触发一次old区的gc。

下面看看GC的情况: MarkSweep是old区的gc策略,大概2个小时会触发一次,每次耗时

3194/112=28.5178ms,不会对应用产生明显的停摆,并且也验证了MaxGCPauseMillis参数的作用

 

 


  • JVM调优-解决native heap持续增长_Memory

  • 大小: 46.3 KB
  • JVM调优-解决native heap持续增长_JVM_02

  • 大小: 49.2 KB
  • JVM调优-解决native heap持续增长_Memory_03

  • 大小: 122.7 KB
  • JVM调优-解决native heap持续增长_JVM_04

  • 大小: 61.5 KB
  • JVM调优-解决native heap持续增长_JVM_05

  • 大小: 31 KB
  • JVM调优-解决native heap持续增长_java_06

  • 大小: 3.9 KB
  • JVM调优-解决native heap持续增长_java_07

  • 大小: 10.1 KB
  • 查看图片附件