不恰当的树数据结构导致内存占用过大(六)

1、案例介绍:

有一个后台PRC服务器,使用64为虚拟机,
内存配置为:

  1. -Xms4g(设定程序启动时占用内存大小)
  2. -Xmx8g(设定程序运行期间最大可占用的内存大小)
  3. -Xmn1g(设置年轻代大小为1G)

整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小

下面这个公式没有加上持久代
xmx=xmn+老年代大小
使用ParNew+CMS的垃圾回收器组合

2、出现的问题

平时对外服务的MinorGC 时间约为30毫秒以内,完全可以接受,但是如果业务上需要每10分钟加载一个约为80MB的数据文件到内存进行数据分析,这些数据会在内存中形成超过100万个HashMap<Long,Long>Entry,这段时间里面Minor GC就会造成超过500毫秒的停顿,这时候就不能接受,
这里你可能就有疑问了,为什么会超过那么长时间呢?
首先案例中也说了,新生代是1G内存
而Eden:From Survivor:To Survivor=8:1:1,那Eden相当于800MB大小内存

再来分析一下一个Map有多大:key和value存放的两个长整型数据就有16B(2x8B),包装成Long对象后分别有8B的,8B的Klass指针,再加8B存储数据的Long值,在组成Map.Entry后,又多16B的对象头,一个8B的next字段和4B的int型的hash字段,为了对齐还要加4B的空白填充,最后还有hashmap对这个Entry的8B的引用。
最后总结出 (Long(24B)x2)+Entry(32B)+HashMap Ref(8B)=88B,而数据也就16B 差不多5倍的扩容。

对于80MB的数据来说,如果转成HashMap<Long,Long>Entry 存在内存中,那就会占500MB差不多的内存大小(只是推算),实际不清楚多大,在Eden区肯定很快Minor GC,但是往Survivor 放?

3、初步的解决方法

(1)自己可以通过设置-XX:PretenureSizeThreshold参数(这个参数只对 SerialParNew两个收集器有效)来设置当对象大于多少时直接放进老年代,减少新生代的复制算法频繁GC,因为复制算法的高效体现在"朝生夕灭"上,而这些HashMap对象肯定存活的时间长一些,

(2)除了上面的大对象直接移入老年代,还可以把Survivor移除掉(参数 -XX:SurvivorRatio=65536-XX:MaxTenuringThreshold=0(年龄阈值),或者 -XX:+AlwaysTenure),新生代的所有对象Minor GC一次后就进入老年代,

这些都是治标不治本的,最大的问题还是数据结构设计的不合理,HashMap空间利用效率太低,才18%

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值