如何避免内存溢出和频繁的垃圾回收
内存管理机制的实现原理,就不细谈了,内存的回收过程很复杂,简单的说就是两个步骤:
- 找到所有可以回收的对象,并进行标记
- 回收后清除或者整理内存碎片
垃圾回收完成后,一般是需要进行内存碎片管理,将不连续的空闲内存移动到一起,以便空出足够的连续内存空间供后续使用。
高并发下的程序为什么会卡死?
高并发的情况下,经常有时候会遇到程序卡死的情况。笔者就曾经遇到过,运维反馈程序CPU 占用飙高,然后去查应用日志,发现输出日志很少,然后查询 GC 日志,发现 Full GC 十分频繁。jstack 线程堆栈,找到卡死线程。最后定位到是一次性从数据库中查询数据太大,导致程序卡死。
高并发情况下,自动内存管理机制,其实更容易触发进程暂停。
比如说,微服务收到一个请求后,执行一段业务逻辑,然后返回响应。这个过程会创建一些对象,响应对象和处理中间业务逻辑中需要使用的对象。在下一次垃圾回收之前,这些没用的对象会一直占用内存的。高并发情况下,我们的程序会十分繁忙,短时间内会创建大量对象,这些对象将迅速占满内存,如果没有内存可以使用,垃圾回收器被迫启动,这样垃圾回收器面临的是占满整个内存的海量对象。回收过程会导致进程长时间暂停,这样就发现 程序卡住了。
高并发情况下如何内存管理?
就回到最初的如何避免内存溢出和频繁的垃圾回收。
- 优化代码中的处理请求的业务逻辑,尽量少创建一次性对象,特别是内存占用大的对象。比如让 Request 对象在业务流程中一直传递下去,而不是每执行一个步骤,就创建一个内存和 Request 相似的新对象。
- 对于需要频繁的使用,或者占用内存较大的对象,可以考虑自行回收并重用这些对象。可以为这些对象加了一个对象池,收到请求后,在对象池内申请一个对象,使用完放回对象池。反复使用,避免频繁触发垃圾回收。
通过上面的方法,可以在一定程度上解决内存溢出和频繁的垃圾回收。
总结
为了避免产生大量的待回收的对象,频繁进行垃圾回收,可以尽量少地使用一次性对象,尽量重用这些对象,来减轻垃圾回收的压力。