分析&回答
堆内存溢出(OutOfMemoryError: Java heap space)
内存溢出是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。
会导致 JVM 内存溢出的一些场景:
- JVM 启动参数堆内存值设定的过小
- 内存中加载的数据量过于庞大(一次性从 Mysql、Redis 取出过多数据)
- 对象的引用没有及时释放,使得JVM不能回收
- 代码中存在死循环或循环产生过多重复的对象实体
方法区内存溢出(outOfMemoryError:permgem space)
加载的类过多,或者使用反射、gclib等这种动态代理生成类的技术,就可能导致该区发生内存溢出(1.8之前)
线程栈溢出(java.lang.StackOverflowError)
- 线程栈时线程独有的一块内存结构,所以线程栈发生问题必定是某个线程运行时产生的错误
- 线程栈溢出是由于递归太深或方法调用层级过多导致的
内存泄漏(Memory Leak)
内存泄漏是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
会导致 Java 内存泄漏的一些场景:
- 长生命周期的对象持有短生命周期对象的引用
- 过度使用静态成员属性(static fields)
- 忘记关闭已打开的资源链接(unclosed Resources)
- 没有正确的重写 equals 和 hashcode 方法。(HashMap HashSet)当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。
开发时的编码建议
- 尽早释放无用对象的引用
- 尽量少用静态变量,因为静态变量存放在永久代(方法区),永久代基本不参与垃圾回收(1.8之前)
- 避免在循环中创建对象
- 使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域
- 开启大型文件或从数据库一次拿了太多的数据很容易造成内存溢出,所以在这些地方要大概计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。
反思&扩展
内存溢出问题一般分为两种:
- 一种是由于大峰值下瞬间创建大量对象而导致的内存溢出;
- 一种是由于内存泄漏而导致的内存溢出。
第一种问题可通过限流来处理,第二种问题需要分析程序是否存在 Bug
喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!