image

编辑人: 流年絮语

2024-10-19

0

859

说说 Java内存溢出与内存泄漏

分析&回答

堆内存溢出(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

喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!

创作类型:
原创

本文链接:说说 Java内存溢出与内存泄漏

版权声明:本站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明文章出处。
分享文章