这个Java示例是否会导致内存泄漏?

这个Java示例是否会导致内存泄漏?,java,memory-leaks,Java,Memory Leaks,我有一个简单的例子。该示例从包含10000000个随机整数的文件f加载ArrayList doLog("Test 2"); { FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis); List<Integer> l = (List<Integer>) ois.readObject(); ois.clos

我有一个简单的例子。该示例从包含10000000个随机整数的文件
f
加载
ArrayList

doLog("Test 2");
{
    FileInputStream fis = new FileInputStream(f);
    ObjectInputStream ois = new ObjectInputStream(fis);
    List<Integer> l = (List<Integer>) ois.readObject();
    ois.close();
    fis.close();
    doLog("Test 2.1");
    //l = null; 
    doLog("Test 2.2");
}
doLog("Test 2.3");
System.gc();
doLog("Test 2.4");
但是当我删除它时,我会得到这个日志

Test 2                          Used Mem = 492 KB   Total Mem = 123 MB
Test 2.1                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.2                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.3                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.4                        Used Mem = 44 MB    Total Mem = 123 MB
已用内存
由以下公式计算:
runTime.totalMemory()-runTime.freemory()

问题:如果
l=null存在,是否存在内存泄漏?

l
不可访问,为什么不能释放它?

上述代码中没有内存泄漏

只要您将代码块放在
{}
中,变量
l
就不在范围内,
列表
就是垃圾收集的候选对象,不管您是否先将其设置为
null

但是,在代码块之后和方法返回之前,
列表处于称为不可见的状态。虽然这是事实,但JVM不太可能自动清空引用并收集
列表的内存。因此,显式设置
l=null
可以帮助JVM在进行内存计算之前收集内存。否则,它将在方法返回时自动发生

对于不同的代码运行,您可能会得到不同的结果,因为您永远不知道垃圾收集器何时运行。您可以建议您认为它应该使用
System.gc()
(甚至可以收集不可见的
列表
,即使不设置
l=null
),但是没有承诺。该文件规定:

调用gc方法意味着Java虚拟机需要 努力回收未使用的对象,以使内存 它们目前可用于快速重用。当控件返回时 从方法调用来看,Java虚拟机已经尽了最大的努力 从所有丢弃的对象中回收空间

问题:如果删除l=null;(没有这一行的 代码),这是内存泄漏吗


不,但是如果你使用这个“模式”,它会帮助gc声明内存。

我认为这里有一点语义问题。“内存泄漏”通常是指通过程序(软件等)将某些数据存储在内存中,并使该程序进入无法再访问内存中数据以清除数据的状态,从而使该内存无法用于将来的使用。据我所知,这是一般的定义

“内存泄漏”一词在现实世界中的使用通常指的是编程语言,在编程语言中,开发人员需要手动为他打算放在堆上的数据分配内存。这样的语言是C、C++、Objto-C(*)等。例如,“MALOC”命令或“新”操作符都为将放置在堆内存空间中的类的实例分配内存。在这样的语言中,如果我们以后想要清理它们使用的内存(当不再需要它们时),就需要保留一个指向这些被如此分配的实例的指针。继续上面的示例,引用使用“new”在堆上创建的实例的指针可以稍后使用“delete”命令从内存中“删除”,并将指针作为参数传递给它

因此,对于此类语言,内存泄漏通常意味着将数据放在堆上,然后:

  • 到达不再有指向该数据的指针的状态 或
  • 忘记/忽略手动“取消分配”堆数据上的数据(通过其指针)
现在,在这种“内存泄漏”定义的上下文中,Java几乎永远不会出现这种情况。从技术上讲,在Java中,垃圾收集器的任务是决定何时不再引用堆分配的实例或何时不在范围内并清理它们。在java中没有C++的“删除”命令,甚至允许开发者手动地从堆中“去分配”实例/数据。即使将实例的所有指针设为null,也不会立即释放该实例的内存,相反,它只会使其成为“垃圾可回收”的,在进行清理时将其留给垃圾回收器线程清理

现在,Java中可能发生的另一件事是永远不要放弃指向特定实例的指针,即使在给定点之后不再需要它们。或者,给某个实例一个范围太大,无法使用它们。这样,它们在内存中的停留时间将超过需要的时间(或者永远,永远意味着直到JDK进程被终止),因此即使从功能的角度来看,它们也不会被垃圾收集器收集。这可能会导致类似于更广泛意义上的“内存泄漏”的行为,“内存泄漏”只是表示“在不再需要的时候内存中有东西,并且无法清理”

现在,正如您所看到的,“内存泄漏”有点模糊,但从我所看到的,您的示例不包含内存泄漏(即使是您没有使l=null的版本)。您的所有变量都位于由accolade块分隔的狭窄范围内,它们在该块内使用,并且在块结束时会超出范围,因此它们将被“正确”地(从程序的功能角度)垃圾收集。正如@Keppil所说:将指针设为null将给GC一个更好的提示,告诉它什么时候清理对应的实例,但即使您从未将其设为null,您的代码也不会(不必要)挂起实例,因此没有内存泄漏

Java内存泄漏的一个典型示例是,将代码部署到Java EE应用程序服务器中时,会产生超出所述应用程序服务器控制范围的线程(对启动Quartz作业的servlet进行成像)。如果应用程序部署和取消部署了多个tim
Test 2                          Used Mem = 492 KB   Total Mem = 123 MB
Test 2.1                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.2                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.3                        Used Mem = 44 MB    Total Mem = 123 MB
Test 2.4                        Used Mem = 44 MB    Total Mem = 123 MB