获得徽章 0
- 不理解JVM为什么要调参数
带垃圾回收的语言,对象生命周期结束总会被回收。所以内存暴涨只有两种情况:
1-手动管理的缓存对象没有做对应的释放,要修
2-确实需要显式手动缓存某些数量的对象,这个是业务需求
如果通过调参掩盖1,相当于只是发烧了吃退烧药而已,病还没好
2-业务就是需要这么多,那你通过调参降低了内存消耗,一定在其他地方用隐性的代价支付了。
想要内存占用低,那就得有更多的copy行为,更频繁的malloc/free,可这只是在指标上好看了。比如grafana能捕捉秒级,甚至500ms级别的指标波动。你通过重复free/malloc,让程序强制释放掉申请的内存,但是free malloc本身是有资源开销的,峰值没有减少,只是因为grafana捕捉不到了。
想要延迟低,比如预先申请内存池,malloc了4G内存, 这4G内存可以被反复利用,避免了free malloc开销。但无可避免的是内存池会“释放不掉”,也不是说释放不掉,是内存占用会更高,因为内存池会被长时间持有,就算对象释放了,申请的内存也还保留在虚拟机内。
反过来,你不希望看到4G这么多消耗。于是强制某个对象创建后必须回收掉,这时候就会涉及到底层的free malloc 内存碎片问题。当然可能很多时候只是因为延迟从5ms变成50ms,对大部分toC业务也没影响。反映在指标上虽然延迟增加了1000% ,但是实际上的感知也只是多了40ms
归根结底,垃圾回收就是引用计数(低效),追踪可达(还在用)不可达(可以回收)(高效)。追踪需要的信息量是有下限的,你不能在【不知道某些信息】的前提下判断某个对象可不可以被回收。而这个【需要知道的信息】,各种算法大同小异,但是数量级是接近的。无论GC算法多么精妙,垃圾回收最高效率都可以视为一个常数k,即吐量*延迟 = k,也就是说,在垃圾回收效率没有质的飞跃之前,吞吐和延迟这俩不可兼容。
能通过调参“不引入副作用解决某个问题”是不可能的……
一定会引入副作用:
要么牺牲程序的内存最大消耗
要么牺牲程序的内存平均消耗
要么牺牲程序的执行延迟展开评论点赞