自学内容网 自学内容网

内存泄漏和内存溢出

内存溢出与内存泄露

内存溢出(Out of Memory,俗称 OOM)和内存泄漏(Memory Leak)是两个不同的概念,但它们都与内存管理有关。

内存溢出是指是指当程序请求分配内存时,由于没有足够的内存空间满足其需求,从而触发的错误。在 Java 中,这种情况会抛出 OutOfMemoryError。

内存泄漏是指程序在使用完内存之后未及时释放,而导致可用的内存空间越来越小。而发生的场景通常是长期存活的对象占用短期存活对象的引用,导致短期存活的对象无法释放内存空间,无法被回收。

什么情况会产生内存溢出?

像一次性创建过多对象造成的堆内存溢出,再或者元空间溢出,抛出 java.lang.OutOfMemoryError:Metaspace。又或者栈深度大于虚拟机所允许的栈深度,造成的栈溢出,抛出 StackOverflowError。

内存溢出的原因

比如说静态集合类引起内存泄漏、单例模式、数据连接、IO、Socket 等连接、变量不合理的作用域、hash 值发生变化、ThreadLocal 使用不当等。

  1. 单例和静态集合类都是因为生命周期与JVM生命周期相同,所以题目引用的对象都不能被回收。

  2. 数据连接,IO,Socket连接都需要手动关闭才可以被释放,如果未关闭则也会造成内存溢出。

  3. 变量不合理的作用域,例如全局变量,在main方法结束后,变量依然没有被释放,这个时候如果有其他变量被这个变量引用,那么其他变量也不可以被释放。例如:

    假设我们有一个简单的Java程序,其中有一个全局变量(作用域大于其使用范围),并且在某些情况下没有及时将对象设置为 null

import java.util.ArrayList;
import java.util.List;
​
public class MemoryLeakExample {
    // 全局变量,作用域大于其使用范围
    private static List<String> globalList = new ArrayList<>();
​
    public static void main(String[] args) {
        // 模拟多次调用某个方法
        for (int i = 0; i < 100000; i++) {
            addToList(String.valueOf(i));
        }
​
        // 假设这里有一些其他操作,但不再使用 globalList
        // 但由于 globalList 是静态的,它仍然占用内存
    }
​
    public static void addToList(String value) {
        globalList.add(value);
    }
}

在这个例子中:

  1. 全局变量 globalList:由于 globalList 是一个静态变量,它的作用域是整个类,而不仅仅是某个方法。这意味着即使 main 方法执行完毕,globalList 仍然会占用内存,因为它在整个程序的生命周期内都是可访问的。

  2. 未及时释放对象:在 main 方法中,我们向 globalList 添加了大量的字符串对象。即使 main 方法执行完毕,这些字符串对象仍然被 globalList 引用,无法被垃圾回收器回收,从而导致内存泄漏。

如果想解决这个问题就需要不要的时候将globalList 设置为null

hash 值放生变化

如果使用 HashSet 或者 HashMap 的时候,由于hash值与存进来的hash值不同,所以就会导致找不到存入的对象,那么也就释放内存,可能导致内存泄漏。

ThreadLocal 使用不当

ThreadLocal 的弱引用导致内存泄漏也是个老生常谈的话题了,使用完 ThreadLocal 一定要记得使用 remove 方法来进行清除。


原文地址:https://blog.csdn.net/m0_73173239/article/details/140464733

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!