文章插图
面试官:说说内存泄露和内存溢出加送个知识点,三连的终将成为大神~~
内存泄露和内存溢出内存溢出(out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个 Integer,但给它存了 Long 才能存下的数,那就是内存溢出 。
内存泄露( memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光 。
memory leak 最终会导致 out of memory!
三、GC overhead limit exceededJVM 内置了垃圾回收机制GC,所以作为 Javaer 的我们不需要手工编写代码来进行内存分配和释放,但是当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 错误(俗称:垃圾回收上头) 。简单地说,就是应用程序已经基本耗尽了所有可用内存,GC 也无法回收 。
假如不抛出 GC overhead limit exceeded 错误,那 GC 清理的那么一丢丢内存很快就会被再次填满,迫使 GC 再次执行,这样恶性循环,CPU 使用率 100%,而 GC 没什么效果 。
3.1 写个 bug出现这个错误的实例,其实我们写个无限循环,往 List 或 Map 加数据就会一直 Full GC,直到扛不住,这里用一个不容易发现的栗子 。我们往 map 中添加 1000 个元素 。
/** * JVM 参数: -Xmx14m -XX:+PrintGCDetails */public class KeylessEntry { static class Key { Integer id; Key(Integer id) { this.id = id; } @Override public int hashCode() { return id.hashCode(); } } public static void main(String[] args) { Map m = new HashMap(); while (true){ for (int i = 0; i < 1000; i++){ if (!m.containsKey(new Key(i))){ m.put(new Key(i), "Number:" + i); } } System.out.println("m.size()=" + m.size()); } }}
...m.size()=54000m.size()=55000m.size()=56000Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
从输出结果可以看到,我们的限制 1000 条数据没有起作用,map 容量远超过了 1000,而且最后也出现了我们想要的错误,这是因为类 Key 只重写了 hashCode() 方法,却没有重写 equals() 方法,我们在使用 containsKey() 方法其实就出现了问题,于是就会一直往 HashMap 中添加 Key,直至 GC 都清理不掉 。 面试官又来了:说一下HashMap原理以及为什么需要同时实现equals和hashcode
执行这个程序的最终错误,和 JVM 配置也会有关系,如果设置的堆内存特别小,会直接报 Java heap space 。算是被这个错误截胡了,所以有时,在资源受限的情况下,无法准确预测程序会死于哪种具体的原因 。3.2 解决方案
- 添加 JVM 参数-XX:-UseGCOverheadLimit 不推荐这么干,没有真正解决问题,只是将异常推迟
- 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码
- dump内存分析,检查是否存在内存泄露,如果没有,加大内存
推荐阅读
- 产品风险评估报告怎么写 淘宝店铺风险分析
- 再见HTML ! 用纯Python就能写一个漂亮的网页
- 纯CSS实现轮播图
- 假设你的文案压根没人看
- 如何写出转化率高的爆款文案
- 写在最美的季节,送给径山的冬日散文诗
- 分析2.5万篇博客后,来看看后端程序员每天都在写什么
- 淘宝卖家给买家的好评怎么写 淘宝店铺好评怎么写
- 淘宝申诉说明范例 淘宝侵权申诉内容怎么写
- Ubuntu18.04部署django3.x