内存溢出 内存泄漏 StackOverflowError

  • StackOverflowError
  • 内存溢出
  • 内存泄漏

1. StackOverflowError

  • 定义:函数调用栈帧太深了,注意代码中是否有了循环调用方法而无法退出的情况

  • 原因:StackOverflowError 是一个java中常出现的错误:在jvm运行时的数据区域中有一个java虚拟机栈,当执行java方法时会进行压栈弹栈的操作。在栈中会保存局部变量,操作数栈,方法出口等等。jvm规定了栈的最大深度,当执行时栈的深度大于了规定的深度,就会抛出StackOverflowError错误

  • 例子:

    public class Test {
        public static void main(String[] args) {
            f();
        }
    
        private static void f() {
            System.out.println("8888");
            f();
        }
    }
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lbmlEUCV-1590418824934)(C:\Users\apple\AppData\Roaming\Typora\typora-user-images\image-20200519122335276.png)]

2.内存溢出(OOM)

  • 定义:内存溢出(Out Of Memory)是指系统已经不能再分配出你所需要的空间,比如你需要100M的空间,系统只剩90M了,这就叫内存溢出。
  • 常见原因:
    1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
    3. 代码中存在死循环或循环产生过多重复的对象实体;
    4. 使用的第三方软件中的BUG;
    5. 启动参数内存值设定的过小;
  • 常见的解决方法:
    • 修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
    • 检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
    • 对代码进行走查和分析,找出可能发生内存溢出的位置。
    • 使用内存查看工具动态查看内存使用情况。

3.内存泄漏

  • 定义:指程序中动态分配内存给一些临时对象,但是对象不会被GC所回收,它始终占用内存。即被已无用。可能导致内存泄漏,虚拟机宁愿抛出OOM也不会去回收指向的对象。内存泄漏次数多了就会导致内存溢出。

    一般我们所说的内存泄漏指的是堆内存的泄露,C++/C中有free函数可以释放内存,java中有垃圾回收机制不用程序员自己手动调用释放,但是如果这块内存因为某些原因无法释放,就不能再用了,此时就可以说这块内存泄漏了。

### Java内存溢出内存泄漏的区别、原因及解决方法 --- #### 1. **内存溢出内存泄漏的区别** - **定义**: - 内存溢出(Out of Memory,OOM)指 JVM 在尝试为对象分配内存时,堆内存不足以满足分配请求,从而抛出 `OutOfMemoryError` 异常[^2]。这是由于程序所需内存超过系统可用内存而导致的最终表现形式。 - 内存泄漏(Memory Leak)则是指程序运行过程中,已经不再使用的对象仍然被持有引用,导致垃圾回收器无法回收这些对象所占的内存。这种现象通常不会立即显现异常,但长期积累可能导致 OOM[^1]。 - **主要差异**: | 特性 | 内存溢出 | 内存泄漏 | |---------------------|-----------------------------------|-----------------------------------| | 表现形式 | 抛出 `OutOfMemoryError` | 不会主动报错 | | 原因 | 系统可用内存不足 | 对象未正确释放 | | 发生时机 | 即刻 | 随时间推移逐渐恶化 | --- #### 2. **内存溢出的原因及解决方案** ##### (1)**原因** - **堆内存溢出 (`java.lang.OutOfMemoryError: Java heap space`)** - 堆内存过小或加载过多大对象超出容量限制。例如频繁创建大量短生命周期的大对象,或者集合类(如 List 或 Map)无限增长。 - 动态扩展数组大小时未能合理规划初始容量。 - **元空间溢出 (`java.lang.OutOfMemoryError: Metaspace`)** - 类加载过多,尤其是动态代理或反射生成大量 Class 文件。 - 使用 `-XX:MaxMetaspaceSize` 参数设置元空间上限不合理。 - **栈内存溢出 (`StackOverflowError`) 和线程上下文切换耗尽内存** - 方法递归层次太深,导致单一线程栈帧数量超标。 - 创建过多线程,每条线程都占用一定量的内存资源。 ##### (2)**解决方案** - 调整 JVM 参数优化内存配置: ```bash java -Xms512m -Xmx1g -XX:MaxMetaspaceSize=256m ... ``` 上述命令分别设置了初始堆大小、最大堆大小以及元空间的最大值。 - 减少不必要的对象实例化次数,避免过度依赖静态变量保存全局状态。 - 利用工具定位问题根源并针对性改进代码逻辑,常用工具有 VisualVM、MAT(Memory Analyzer Tool) 等。 --- #### 3. **内存泄漏的原因及解决方案** ##### (1)**原因** - **静态域持有的无用对象引用** - 当某个类中存在静态成员变量指向外部对象时,在该类卸载之前,GC 就无法清理掉这个外层对象。即使局部作用范围内已完成对该对象的操作,但由于静态引用的存在,它依然占据着宝贵的内存资源[^4]。 - **缓存机制设计缺陷** - 缓存策略缺乏淘汰规则,随着时间流逝不断累积新加入的数据项却从未清除旧记录。 - **监听器注册后未注销** - GUI 开发环境下经常遇到这种情况,如果组件销毁之后忘记解除事件绑定关系,则对应的回调函数依旧保留下来成为潜在隐患之一。 - **ThreadLocal滥用** - 每个线程都有独立副本存储于 ThreadLocalMap 中,一旦线程结束却没有及时清空其中的内容就会造成严重的浪费甚至泄露风险。 ##### (2)**解决方案** - 审核所有涉及长时间存活的对象容器是否有必要维持强引用关系,考虑改用弱引用(`WeakReference`)代替默认行为来降低影响程度。 - 设计合理的 LRU/LFU 缓存算法确保有限容量内的高效周转。 - 明确责任划分原则,在适当位置显式调用 removeListener() 接口完成解绑动作。 - 定期检查遗留下来的 ThreadLocals 是否还有实际用途,必要时强制置 null 并触发 GC 过程。 --- #### 总结对比表格 | 方面 | 内存溢出 | 内存泄漏 | |-----------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | 描述 | JVM 的总内存不够分配新的对象 | 已经不需要的对象还一直被某些地方引用 | | 错误提示 | OutOfMemoryError | 一般不会有明显错误 | | 主要成因 | 数据结构过大、文件上传下载过程中的缓冲区尺寸设定失误 | 非法持有对象引用 | | 解决思路 | 提升硬件规格、调整 JVM 参数 | 修改源码消除非法引用 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值