大多数程序员都知道,使用像Java这样的编程语言的好处之一是,他们不再需要担心分配和释放内存。您只需创建对象,当应用程序不再需要它们时,Java会通过一种称为垃圾收集的机制来移除它们。那么困扰其他编程语言的一个棘手问题——可怕的内存泄漏。在Java语言中还有吗?
在我们深入讨论之前,让我们先回顾一下垃圾收集是如何工作的。垃圾收集器的工作是找到应用程序不再需要的对象,并在它们不能被访问或引用时将其删除。垃圾收集器从根节点开始,根节点是在Java应用程序的整个生命周期中持续存在的类,它会遍历所有被引用的节点。在遍历节点时,它会跟踪哪些对象正在被引用。任何不再被引用的类都有资格被垃圾收集。当对象被删除时,这些对象使用的内存资源可以返回给Java虚拟机。
因此,Java代码确实不需要程序员来负责内存管理清理,并且它会自动垃圾收集未使用的对象。然而,要记住的关键一点是一个对象只有在不再被引用时才被认为是未使用的。图1说明了这个概念。
在上图中在执行一个Java应用程序期间具有不同生存期的两个类。Class A首先被实例化,并且在程序的整个生命周期中存在很长时间。在某个时刻,Class B被创建,Class A添加对此新创建的类的引用。现在让我们假设Class B是一些用户界面小部件,被用户显示并最终消除。尽管Class B不再需要,也应该被回收了,如果引用Class B的Class A没有被清除,Class B即使在下一个垃圾收集周期执行之后,也将继续存在并占用内存空间,那么这个时候Class B的对象实例就发生了内存泄露。
什么时候内存泄漏是一个问题?
如果程序执行一段时间后抛出一个`java.lang.OutOfMemoryError,内存泄漏肯定是一个很大的疑点。那么什么时候内存泄漏应该成为一个问题?完美主义程序员会回答这个问题全部需要调查和纠正内存泄漏。然而,在得出这个结论之前,还有几个其他的问题需要考虑,包括程序的生命周期和泄漏的大小。
考虑垃圾收集器在应用程序生命周期中可能永远不会运行的可能性。无法保证JVM何时或是否会调用垃圾收集器——即使程序显式调用 System.gc()
。通常,垃圾收集器不会自动运行,直到程序需要比当前可用内存更多的内存。此时,JVM将首先尝试通过调用垃圾收集器来获得更多的可用内存。如果这种尝试仍然没有释放足够的资源,那么JVM将从操作系统获得更多的内存,直到它最终达到允许的最大值。
以一个小型的Java应用程序为例,它显示一些简单的用户界面元素用于配置修改,并且存在内存泄漏。很有可能在应用程序关闭之前,垃圾收集器甚至不会被调用,因为JVM可能有足够的内存来创建程序所需的所有对象,并有剩余的内存来备用。因此,在这种情况下,即使一些死对象在程序执行时占用了内存,但对于所有实际用途来说,这真的无关紧要。
如果正在开发的Java代码打算一天24小时在服务器上运行,那么内存泄漏比我们的配置实用程序的情况要严重得多。即使是某些代码中本来应该连续运行的最小漏洞,最终也会导致JVM耗尽所有可用的内存。
最后一个考虑是,Java内存泄漏不应该被视为像其他语言(如C++)中发生的那样危险,因为在其他语言中,内存会丢失,永远不会返回到操作系统。在Java应用程序的情况下,对象都是依附在操作系统给JVM的内存资源上。因此,理论上,一旦Java应用程序及其JVM关闭,所有分配的内存都将返回给操作系统。
常见内存泄露情况
长生命周期的对象持有短生命周期对象的引用
例如将ArrayList设置为静态变量,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏
连接未关闭
如数据库连接、网络连接和IO连接等,只有连接被关闭后,垃圾收集器才会回收对应的对象。
内部类持有外部类
Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏
如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏(你认为垃圾收集器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾收集器不能正常工作)
解决方法:你可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收;
内存泄漏和内存溢出辨析
内存溢出和内存泄露,在程序运行时,都会表现出OOM异常,但是两者还是有本质不同的:
内存溢出是实实在在的内存空间不足导致;内存泄漏则是该释放的对象没有释放,常见于使用容器保存元素的情况下。
如何避免呢?内存溢出需要我们检查代码以及设置足够的空间,内存泄漏则一定是代码有问题。
而且往往很多情况下,内存溢出的根源往往是内存泄漏。
往期精选
CHOICENESS
![9d7d88a55f2c2f3d713ab56de81ee289.png](https://i-blog.csdnimg.cn/blog_migrate/52e62da304eabb02538ac03a74061e05.png)
![885bb686ffdf9d9a177d197b97bca246.gif](https://i-blog.csdnimg.cn/blog_migrate/651c421641c5fd940ddf517586825ea5.gif)