内存泄漏和内存溢出
内存溢出与内存泄露
内存溢出(Out of Memory,俗称 OOM)和内存泄漏(Memory Leak)是两个不同的概念,但它们都与内存管理有关。
内存溢出是指是指当程序请求分配内存时,由于没有足够的内存空间满足其需求,从而触发的错误。在 Java 中,这种情况会抛出 OutOfMemoryError。
内存泄漏是指程序在使用完内存之后未及时释放,而导致可用的内存空间越来越小。而发生的场景通常是长期存活的对象占用短期存活对象的引用,导致短期存活的对象无法释放内存空间,无法被回收。
什么情况会产生内存溢出?
像一次性创建过多对象造成的堆内存溢出,再或者元空间溢出,抛出 java.lang.OutOfMemoryError:Metaspace
。又或者栈深度大于虚拟机所允许的栈深度,造成的栈溢出,抛出 StackOverflowError。
内存溢出的原因
比如说静态集合类引起内存泄漏、单例模式、数据连接、IO、Socket 等连接、变量不合理的作用域、hash 值发生变化、ThreadLocal 使用不当等。
-
像单例和静态集合类都是因为生命周期与JVM生命周期相同,所以题目引用的对象都不能被回收。
-
而数据连接,IO,Socket连接都需要手动关闭才可以被释放,如果未关闭则也会造成内存溢出。
-
变量不合理的作用域,例如全局变量,在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); } }
在这个例子中:
-
全局变量
globalList
:由于globalList
是一个静态变量,它的作用域是整个类,而不仅仅是某个方法。这意味着即使main
方法执行完毕,globalList
仍然会占用内存,因为它在整个程序的生命周期内都是可访问的。 -
未及时释放对象:在
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)!