理解Java内存模型下存在序列化的棘手情况

理解Java内存模型下存在序列化的棘手情况,java,memory,serialization,garbage-collection,Java,Memory,Serialization,Garbage Collection,您好,我很难理解以下代码在序列化下的行为 Foo被创建为一个临时对象,并最终用于Bar的匿名后代的方法中。Bar是一个字段,在创建Foo对象的方法中仍然有效 Foo对象是如何存储的。它是bar对象的“隐式字段”吗?它是程序对象的“隐式字段”吗?它是否只是漂浮在堆上,而只在Bar::doSomething实现的代码中被引用 我假设垃圾收集器足够聪明,不会删除这个对象。但是,在序列化和反序列化之后,Bar对象还会指向同一个对象吗?Foo不可序列化,因此无法序列化自身 对于那些想知道的人来说,这个示例

您好,我很难理解以下代码在序列化下的行为

Foo被创建为一个临时对象,并最终用于Bar的匿名后代的方法中。Bar是一个字段,在创建Foo对象的方法中仍然有效

Foo对象是如何存储的。它是bar对象的“隐式字段”吗?它是程序对象的“隐式字段”吗?它是否只是漂浮在堆上,而只在Bar::doSomething实现的代码中被引用

我假设垃圾收集器足够聪明,不会删除这个对象。但是,在序列化和反序列化之后,Bar对象还会指向同一个对象吗?Foo不可序列化,因此无法序列化自身

对于那些想知道的人来说,这个示例是从我正在编写的Wicket应用程序中派生出来的,在我的例子中,Foo和Bar是模型。因此,Foo也是可序列化的,但当Foo不可序列化时,问题似乎更有趣

class Foo {
  public void doSomethingElse() {
  }
}

abstract class Bar implements Serializable {
  public abstract void doSomething();
}

class Program {
  Bar bar;

  void main(String[] args) {
    otherMethod(new Foo());
  }

  void otherMethod(Foo foo) {
    bar = new Bar() {
      @Override
      public void doSomething() {
        foo.doSomethingElse();
      }
    };
  }

  // much later after bar has been serialized and deserialized
  void calledMuchLater() {
    bar.doSomething();
  }
}

如果
Foo
不可序列化,这将不起作用。您将得到一个
java.io.NotSerializableException

如果
Foo
可序列化,则它将与
Bar一起序列化并成功反序列化


Foo对象是如何存储的?它是bar对象的“隐式字段”吗

我不知道具体是怎么做的,我认为这并不重要。重要的是它是被存储的(iff
Foo
是可序列化的;否则您会得到一个异常,正如我上面所说的)

它是否只是漂浮在堆上,而只在Bar::doSomething实现的代码中被引用

它不会四处飘浮,它在堆上有自己的位置。只要存在对此对象的活动引用,垃圾收集器就不会拾取它。由于
foo.doSomethingElse()
引用堆上的真实foo对象,因此它将留在那里(实际上,这是创建内存泄漏的一种方法)

但是,在序列化和反序列化之后,Bar对象还会指向同一个对象吗


否。将创建一个类型为
Bar
的新对象,恢复序列化对象的状态。

这里实际上会有两个问题。首先,您正在创建的Bar类的实例是Program的内部类,并且Program不可序列化。如果尝试序列化匿名内部类,即使不担心Foo,也会出现错误


创建匿名内部类时,该类代码中引用的任何变量都会通过Java透明生成的构造函数复制到该类中。这些变量成为该类的实例字段,因此必须可序列化才能序列化该类。

您的程序甚至无法运行
main
不是静态方法。由于
Foo
参数不是
final
创建匿名内部类时,该类代码中引用的任何变量都会通过Java透明生成的构造函数复制到该类中。这些变量成为该类的实例字段,因此必须可序列化才能序列化该类“
这让我非常清楚,我仍然不明白内存泄漏是如何发生的。只是程序保存了对某个点上可能不需要的Bar对象的引用吗?或者是否存在一些无法使用但仍然没有垃圾收集的对象?Java中的内存泄漏与其他语言(如C)不同,在其他语言中,程序员必须小心地释放程序分配但不再需要的内存。从理论上讲,垃圾收集通过清理程序中不再引用的对象并释放分配给它们的内存来帮助解决大多数常见问题。在Java中创建内存泄漏的唯一真正方法是分配您不需要的对象和/或在它们不再有用后保留它们。一种常见的方法是将不需要的对象存储在集合中。创建的对象是Bar的匿名子类型。