【问题标题】:Hypothetical partial object dealocation in javajava中假设的部分对象释放
【发布时间】:2021-09-29 12:34:30
【问题描述】:

以下是一个假设问题。我很感兴趣,如果可以通过任何可以想象的晦涩手段(不安全的 API、JNI、ASM 等)以某种方式实现效果。这不是 XY 问题,我从不打算编写这样的代码!我只是对内部结构感到好奇。

假设我们在 Java 中有这个非常简单的层次结构:

class Cupcake {
    public String kind;
    // ...
}

class WholeRealityItself extends Cupcake {
    public Object[] wholeMatterOfUniverse;

    // transform internal state because reasons
    public performBigBangAndFluctuateACupcake() {
        // ... chaotic spacetime fluctuations produce a cupcake
        this.kind = "quantum_with_sprinkles";
    }
}

我们的流程如下:

WholeRealityItself reality = new WholeRealityItself();
reality.performBigBangAndFluctuateACupcake();
Cupcake cupcake = (Cupcake) reality; // upcast
// from now on the object will be only accessed via it's supertype and never downcast

言归正传:

  1. 我们创建一个子类型的对象,它分配了很多内存。
  2. 我们对此子类型对象执行一些内部状态转换。
  3. 我们将对象向上转换为它的超类型,从现在开始,我们将只通过它的超类型来引用它,并且永远不会向下转换

所以现在我们的 JVM 持有一个 Cupcake 对内部 WholeRealityItself 对象的引用,该对象的内存(程序员知道)将永远不会再被访问。是的,我知道引用和实际分配的对象是两个不同的东西,向上转换/向下转换使程序只是“重新解释”一个对象。

完全忽略了这样一个事实,即这种可憎的代码是不可用的,应该用理智的构建器/工厂/副本或其他东西代替,只是为了论证的缘故假设我们想要这样。 重点不在于如何达到同样的效果,而是以下是否可行:

您能否强制缩小实际分配的 OBJECT 以将其内部结构从 WholeRealityItself 转换为 Cupcake 并强制释放 wholeMatterOfUniverse

AKA - 你能以某种方式 对底层分配的OBJECT 进行切片吗?关于对象切片的最后一个问题来自大约 10 年前。

【问题讨论】:

  • 好吧,为了让这在理论上成为可能,需要 WholeRealityItself 成为 final,这样我们就可以保证没有人可以编写一个仍然可以访问 wholeMatterOfUniverse 的子类,例如通过在 equals 的覆盖中使用它。
  • 但我很确定在 JVM 中不可能有这样的事情 - 例如,这将为 C++ 中可能遇到的所有对象切片问题打开大门,而 Java 旨在避免这样的陷阱。
  • 即使您编辑中的措辞是错误的:转换不会使 Java 重新解释一个对象(这就是它在 C 中的工作方式以及在 C++ 中的某些转换)。但在 Java 中,演员表只会重新解释引用。这是一个细微的差别,但由于您询问的是处于可实现性边缘的功能,因此这种区别很重要。

标签: java memory-management allocation


【解决方案1】:

您正在谈论向上转换,而您的对象没有任何反应。唯一的释放方法是在 cupecake 中有一个构造函数,它将另一个 cupecake 作为输入,并且只使用所需的部分。在你释放整个世界之后。

【讨论】:

    【解决方案2】:

    向上转换和向下转换都不会改变对象本身 - 它们只会影响编译器如何处理对对象的引用。

    一个例子是重写方法:调用什么方法完全取决于对象的运行时类型,而不是编译器使用的引用类型:

    class Cupcake {
        public String kind;
        // ...
        public void printMeOut() {
            System.out.println("Cupcake");
        }
    }
    
    class WholeRealityItself extends Cupcake {
        public Object[] wholeMatterOfUniverse;
    
        @Override
        public void printMeOut() {
            System.out.println("WholeRealityItself");
        }
        public performBigBangAndFluctuateACupcake() {
             //...
        }
    }
    

    在你的示例代码之后

    WholeRealityItself reality = new WholeRealityItself();
    reality.performBigBangAndFluctuateACupcake();
    Cupcake cupcake = (Cupcake) reality; // upcast
    // from now on the object will be only accessed via it's supertype and never downcast
    

    无论上播后经过多长时间,现在每次调用 cupcake.printMeOut(); 都会打印出“WholeRealityItself”。

    【讨论】:

    • 我知道,我问的是下面的抽象层——实际分配的对象切片(所以更多的内存管理内部)——这可能吗?
    • @Tooster:不,不是你描述的那样。如果你有 reality.getCupcake() 这样的东西而不是演员表,那么你可以通过只返回你想要保留的数据来使其工作。
    【解决方案3】:

    AKA - 你能以某种方式对底层分配的对象进行切片吗?

    不,你不能。

    对象由堆节点表示。如果你做了任何干扰堆节点的大小或类型(类)的事情,你很可能会导致垃圾收集器崩溃。

    我想,您可以使用抽象破坏(讨厌!)反射来识别null 并将其分配给子类添加的所有字段。但问题是你不能对子类重载的超类的方法做任何事情。如果这些方法引用了您已将 null 分配给的任何字段,并且 something 调用了它们,那么您有一个可能损坏(粉碎)的对象。

    我的建议:使用 WholeRealityItself 对象的相关状态创建一个全新的 Cupcake 对象。会有不同的参考。处理它。

    这个David Wheeler quotation 可能是相关的...

    “计算机科学中的所有问题都可以通过另一个层次的间接性来解决。”

    【讨论】:

      猜你喜欢
      • 2011-07-30
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多