CodeGym /Java 博客 /随机的 /对象生命周期
John Squirrels
第 41 级
San Francisco

对象生命周期

已在 随机的 群组中发布
你好!如果我告诉您您的计算机内存量有限,我想您不会感到惊讶 :)
对象生命周期 - 1
即使是您的硬盘(其大小是 RAM 的很多很多倍)也可能会塞满您最喜欢的游戏、电视节目和其他内容。为防止这种情况发生,您需要监控计算机内存的当前状态并删除不需要的文件。所有这些与 Java 编程有什么关系?很直接!毕竟,创建任何对象都会导致 Java 机器为其分配内存。一个大型的现实世界程序会创建数万或数十万个对象,并为每个对象分配一块内存。但是您怎么看,这些对象中有多少存在?在我们的程序运行时,它们是否一直“活着”?当然不是。即使具有所有优点,Java 对象也不是不朽的 :)对象有自己的生命周期。 今天我们将暂停编写代码并探索这个过程 :) 这对于理解程序如何工作和管理资源也非常重要。那么,一个物体的生命从哪里开始呢?就像人一样,从出生开始,即当它被创造时。

Cat cat = new Cat();// Our Cat object's lifecycle begins now!
首先,Java 虚拟机分配创建对象所需的内存。然后它创建一个对它的引用(在我们的例子中是cat),以便跟踪它。然后初始化所有变量,调用构造函数,我们的新对象现在过着自己的生活:)对象的生命周期各不相同。这里没有确切的数字。在任何情况下,对象都存在于程序中并在一段时间内执行其功能。准确地说,只要有对它的引用,这个对象就是“活着的”。一旦没有引用,对象就会“死亡”。例如:

public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}
在该main()方法中,“Lamborghini Diablo”Car 对象在第二行不再存在。只有一个对它的引用,并且该引用被设置为 null。由于没有剩余的暗黑破坏神参考,它变成了“垃圾”。不必将引用设置为零即可发生这种情况:

public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}
这里我们创建了第二个对象并将其分配给 lamborghini 引用。现在有两个引用指向该Lamborghini Gallardo对象,但该Lamborghini Diablo对象没有。这意味着Diablo对象变成了垃圾。这是 Java 的内置垃圾收集器(GC) 发挥作用的时候。
对象生命周期 - 2
垃圾收集器是一种内部 Java 机制,负责释放内存,即从内存中删除不需要的对象。我们选择用扫地机器人来代表它是有原因的。垃圾收集器的工作方式大致相同:它在后台“移动”您的程序,收集垃圾。您几乎不必与它互动。它的工作是删除程序中不再使用的对象。因此,它为其他对象释放了内存。您还记得我们在课程开始时说过在现实生活中您必须监控计算机的状态并删除旧文件吗?如果我们谈论的是 Java 对象,垃圾收集器会为您做这件事. 垃圾收集器在您的程序运行时多次启动:您不必显式调用它并给它命令(尽管这在技术上是可行的)。稍后我们将更多地讨论垃圾收集器,并更详细地分析它是如何工作的。当垃圾收集器到达一个对象时——就在它被销毁之前——finalize()调用该对象的特殊方法。可以调用此方法来释放对象使用的某些额外资源。该finalize()方法属于 Object 类。换句话说,它类似于equals(), hashCode()and toString()(您之前遇到过)。每个对象都有它。和其他方法不同的是……这怎么说呢……很任性。我们的意思是它并不总是在对象被销毁之前调用。编程是一项非常精确的活动。程序员告诉计算机做某事,计算机就会去做。finalize()我假设您已经习惯了这种行为,所以一开始您可能很难接受以下想法:“在对象被销毁之前,调用Object 类的方法。或者不调用。如果我们走运的话!“ 不过,这就是现实。Java 机器根据具体情况自行决定是否调用 finalize()。作为实验,让我们尝试运行以下代码:

public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {

       for (int i = 0 ; i < 1000000; i++) {

           Cat cat = new Cat();
           cat = null;// The first object becomes available for garbage collection here
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("The Cat is destroyed!");
   }
}
我们创建一个Cat对象,并在下一行中将对它的唯一引用清零。我们这样做了一百万次。我们已经明确地覆盖了该finalize()方法。每次Cat对象被销毁时,它都必须显示一个字符串——总共一百万次。但不是!确切地说,在我的电脑上只执行了37346次!换句话说,我的 Java 机器决定finalize()在每 27 个案例中只有 1 个调用该方法。在其他情况下,垃圾回收不涉及此调用。尝试自己运行这段代码。您很可能会得到不同的结果。如您所见,很难找到finalize()可靠的合作伙伴 :) 因此,这里有一个关于未来的小提示:不要依赖finalize()释放关键资源的方法。JVM 可能会调用它,也可能不会。谁知道?如果您的对象在存在时持有一些对性能至关重要的资源(例如,一个打开的数据库连接),那么最好创建并显式调用一个特殊的方法来在不再需要该对象时释放它们。这样,您就可以确定您的程序的性能不会受到影响。 我们一开始就说使用内存和垃圾收集是非常重要的主题,确实如此。资源处理不当和误解如何清理不必要的对象会导致最令人不快的错误之一:内存泄漏。这是最著名的编程错误之一。它甚至有自己的维基百科文章. 编写不当的代码可能会导致每次都为新创建的对象分配内存,但旧的、不必要的对象无法用于垃圾回收。既然我们已经用机器人吸尘器做了类比,想象一下如果在运行机器人之前你把袜子撒得到处都是,打碎了一个玻璃花瓶,把乐高积木积木弄得满地都是,会发生什么。自然地,机器人会尝试做某事,但总有一天它会卡住。
对象生命周期 - 3
为了让真空吸尘器正常运行,您需要保持地板的良好形状并捡起它无法处理的所有东西。垃圾收集器遵循相同的原则。如果一个程序有很多它无法清理的对象(比如我们的机器人吸尘器的袜子或乐高积木),总有一天我们会耗尽内存。不仅您的程序会挂起,所有其他恰好在计算机上运行的程序也会挂起。毕竟,它们也不会有足够的内存(回到我们的类比,地板上的碎玻璃不仅会停止真空吸尘器,还会停止住家里的人)。简而言之,这就是 Java 中对象生命周期和垃圾回收的样子。您无需记住这一点:只需了解其工作原理就足够了。下一节课,我们 我们将更详细地返回这些过程。但现在,您可以返回解决 CodeGym 任务 :) 祝你好运!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION