为什么即使外部 class 对象被销毁,内部 class 实例仍保留在内存中?

Why does an inner class instance remian in the memory even if the outer class object is destroyed?

请考虑以下两个 class:

a.) 学生

package datatypes;

public class Student {

    private String name ;

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

    class Address{
        String city;
        String state;

        Address(String city , String state){
            this.city = city;
            this.state = state;
        }

        String getAddress(){
            return city + state;
        }

    }

    String getName(){
        return name;
    }
}

b.) StudentDemo

package datatypes;

import datatypes.Student.Address;

public class StudentDemo {
    public static void main(String[] args) {
        Student obj = new Student("Yati");
        Address adr = obj.new Address("YNR" , "HARYANA");

        System.out.println(obj.getName());
        System.out.println(adr.getAddress());

        obj=null;

        //System.out.println(obj.getName());
        System.out.println(adr.getAddress()); //Line 16

    }
}

根据oracle java docs

For creating an instance of the inner class first we have to create an instance of the enclosing class and later we can create the instances of the inner class.

与其他成员(实例变量和实例方法)一样,内部class也是外部class实例的成员。在 的第 16 行 StudentDemo.java 中,即使负责创建 Address[= 的对象,我仍然能够打印地址的值37=] 对象 存在于内存中。

我的问题是:为什么 Address 对象保留在内存中并且在 obj 设置为 null 后不会自动销毁?

输出:

Yati
YNRHARYANA
YNRHARYANA

设置对null的引用并不意味着垃圾收集器立即从内存中收集对应的object!

除此之外:GC 甚至无法收集您的 object;因为 adr 仍然有对 "inner object" 的引用;并且那个引用了它的外部所有者!

因此,即使您在执行 obj = null 之后添加一个 System.gc() 调用...... object 也 不会 被收集。 (仅作记录:调用 gc() 只是 JVM 到垃圾收集器 运行 的 "hint";不能保证任何事情都会发生)

长话短说:当然事情必须这样进行。您的代码包含对 inner object 的引用;虽然该引用存在,但 object 必须存在。虽然 inner object 必须存在;它的 outer parent 也必须如此。换句话说:整个系统 依赖于 inner/outer object 的生命周期 紧密耦合的事实!

考虑到你最后的评论 - 运行时间应该如何知道这个 inner object 可以与其 一起生活outer parent 为空?!您可能会在 inner 上调用一个实际使用 outer.this 的方法;然后是什么,NullPointerException?!

所以真正的答案可能是理解你问的是一个纯粹的理论问题。在 "real" 世界中,您根本不会那样做。在现实世界中,您不会分发对内部 object 的引用;不关心他们的外在 parent。类似于域驱动设计——object 的 聚合 通过 root[=聚合的 46=] object(参见 here 示例)。这当然与 JVM 中的 objects 不同;但如前所述:一个例子,您只是出于 概念性 原因而做不同的事情。

除了对您的内在 class 的引用仍在使用之外,您所有的内在和外在 class 恶作剧都是转移视线。

事实仍然是写作

Foo f = new Foo();
f = null;

对于任何 class Foo,只会 导致 Foo 的创建实例被 调度 用于垃圾回收。该对象不一定会立即销毁。

if the object which was responsible for the creation of Address object DOES NOT exist in the memory

当然可以:它是从 adr 引用的,并且 adr 是可访问的,所以它不能被垃圾回收。就算不引用,也不一定会马上收起来。

当不再有对对象的引用或在它参与的引用图中不再有对任何对象的引用时,对象有资格销毁。两个简单的情况:

(o) 单个 "lost" 对象

(o1) <- (o2) 两个 "lost " 对象

在第二种情况下,如果您对 (o1) 有引用,那么 (o1) 不会丢失,但 (o2) 会丢失。

一个内部对象有一个对其封闭的外部对象的引用,并且由于您有一个对内部对象的引用,因此存在一个对其外部对象的 "living" 引用。你有这样的东西:

(outer <- (inner) <-)----some reference