我正在阅读“有效的 Java ”。
在关于 finalize 的讨论中,他说
C++ 析构函数也用于回收其他非内存资源。在 Java 中,try finally 块通常用于此目的。
什么是非内存资源?
数据库连接是非内存资源吗?保持数据库连接的对象不会占用一些内存吗?
我正在阅读“有效的 Java ”。
在关于 finalize 的讨论中,他说
C++ 析构函数也用于回收其他非内存资源。在 Java 中,try finally 块通常用于此目的。
什么是非内存资源?
数据库连接是非内存资源吗?保持数据库连接的对象不会占用一些内存吗?
数据库连接、网络连接、文件句柄、互斥锁等。当你完成它时需要释放(不仅仅是垃圾收集)的东西。
是的,这些对象通常会占用一些内存,但关键是除了内存之外,它们还可以(可能是独占)访问某些资源。
数据库连接是非内存资源吗?
是的,这是最常见的例子之一。其他是文件句柄、本地 GUI 对象(例如 Swing 或 AWT 窗口)和套接字。
保持数据库连接的对象不会占用一些内存吗?
是的,但重点是资源的非内存部分也需要释放,并且通常比对象使用的相对少量的内存要稀缺得多。通常,此类对象具有finalize()
释放非内存资源的方法,但问题是此终结器仅在对象被垃圾回收时运行。
由于对象很小,可能有大量可用的堆内存,因此垃圾收集器很少运行。在垃圾收集器运行之间,非内存资源不会被释放,您可能会用完它们。
这甚至可能导致只有单个对象的问题:例如,如果您想通过打开文件、打开目标文件、复制数据然后删除原始文件来在文件系统之间移动文件,如果文件被删除,则删除将失败仍然打开 - 几乎可以肯定的是,如果您只将对输入流的引用设置为 null 并且不close()
显式调用,因为垃圾收集器不太可能在对象变得符合条件之间的正确点运行用于垃圾收集和调用delete()
Java 自动内存管理的另一个重要 内容涉及一些基本要素。
在我看来,这个问题最好反过来回答——“为什么我不需要手动释放内存”。
这就提出了一个问题,“为什么我需要释放任何资源?”
从根本上说,您正在运行的程序使用多种形式的资源来执行和工作(CPU 周期、内存位置、磁盘访问等)。几乎所有这些都受到“稀缺性”的影响,也就是说,任何此类资源都有一个固定的可用资源池,如果所有资源都被分配,那么操作系统就无法满足请求,通常你的程序无法继续并且非常不优雅地死去——可能使整个系统不稳定。想到的唯一不稀缺的是 CPU 周期,您可以根据需要发布任意数量的 CPU,您只受发布它们的速率的限制,它们不会同时消耗感觉内存或文件句柄是。
因此,您使用的任何资源(内存、文件句柄、数据库连接、网络套接字等)都来自固定数量的此类资源(避免使用“池”一词)和作为您的程序(并且,记住其他程序,更不用说操作系统本身)分配这些资源,可用的数量减少。
如果一个程序请求并被分配了资源并且从不释放它们以供其他地方使用,最终(通常很快)系统将耗尽这些资源。此时,要么系统停止,要么有时会突然终止有问题的程序。
90 年代以前,资源管理(至少在主流开发中)是每个程序员都必须明确处理的问题。一些资源分配管理并不太难,主要是因为分配已经抽象(例如文件句柄或网络套接字),并且可以获取资源,使用它并在不再需要时显式释放它。
但是,管理内存非常困难,特别是在设计时(在非平凡的情况下)无法计算内存分配,而可以通过这种方式管理数据库连接。(无法知道您将使用多少内存,很难/不可能知道何时不再使用内存分配)。此外,内存分配往往会徘徊一段时间,大多数其他资源分配都被限制在一个狭窄的范围内,通常在单个 try 块或方法内,最多通常是一个类。因此,供应商开发了抽象内存分配并将其置于单一管理系统下的方法,由执行环境而不是程序处理。
这是托管环境(例如 Java、.NET)和非托管环境(例如 C、C++ 直接通过操作系统运行)之间的区别。在 C/C++ 中,内存分配是显式完成的(使用 malloc()/new 和相关的重新分配),这会导致各种问题——我需要多少?我如何计算何时需要更多/更少?如何释放内存?如何确保我没有使用已经释放的内存?如何检测和管理内存分配请求失败的情况?如何避免覆盖内存(甚至可能不是我自己的内存)?所有这些都非常困难,并导致内存泄漏、核心转储和各种半随机、不可重现的错误。
因此,Java 实现了自动内存管理。程序员只是简单地分配一个新的对象,既不感兴趣,也不应该关心分配内存的内容或位置(这也是托管环境中没有太多指针方式的原因):
object thing = new Object();
这就是所有需要做的。JVM 将跟踪哪些内存可用、何时需要分配、何时可以释放(因为它不再使用),提供尽可能优雅地处理内存不足情况的方法(并将任何问题限制在执行线程/JVM,而不是关闭整个操作系统)。
自动内存管理是现在大多数编程的标准,因为内存管理是迄今为止最难管理的资源(主要是因为其他资源已经在某种程度上抽象出来了,数据库连接池、套接字抽象等)。
因此,要回答这个问题,是的,您需要管理所有资源,但在 Java 中您不需要(也不能)自己显式管理内存(尽管在某些情况下值得考虑,例如设计缓存)。这留下了您需要明确管理的所有其他资源(这些是非内存资源,即除了对象实例化/销毁之外的所有资源)。
显然,所有这些其他资源都包含在内存资源中,但这不是这里的问题。例如,您可以打开有限数量的数据库连接,您可以创建有限数量的文件句柄。您需要管理这些的分配。finally 块的使用允许您确保资源被释放,即使发生异常也是如此。
例如
public void server()
{
try
{
ServerSocket serverSocket = new ServerSocket(25);
}
catch (Exception exception)
{
// Something went wrong.
}
finally
{
// Clear up and deallocate the unmanaged resource serverSocket here.
// The close method will internally ensure that the network socket is actually flushed, closed and network resources released.
serverSocket.close();
// The memory used by serverSocket will be automatically released by the JVM runtime at this point, as the serverSocket has gone out-of-scope- it is never used again, so can safely be deallocated.
}
}