【问题标题】:how can I subclass ByteBuffer?我怎样才能继承 ByteBuffer?
【发布时间】:2011-10-10 17:20:08
【问题描述】:

所以 Java NIO 架构师没有做一个ByteBuffer 接口,而是一个ByteBuffer class,它不是最终类,但它没有包公共构造函数,因此它不能被子类化在它的包装之外。菲。 :P

我有一个程序在很多地方使用内存映射文件字节缓冲区(通过 FileChannel.map() 获得),我试图找出一个令人讨厌的错误,其中有问题的文件处于打开状态,因为至少有一个 ByteBuffer 未释放到垃圾回收中。

我希望喜欢创建一个看起来像字节缓冲区的InstrumentedByteBuffer 类,但装饰一个常规的ByteBuffer(或其子类,例如MappedByteBuffer)并跟踪它的存在(包括由 duplicate()slice() 创建的新缓冲区)——这样我可以保持使用 ByteBuffer 的代码完整,我只需要装饰原始字节缓冲区。

有没有办法(通过反射或代理或其他方式)绕过私有构造函数?我不需要将它运送到最终产品中,我只需要暂时使用它来解决这个错误。

【问题讨论】:

  • 我想知道是否有像YourKit 这样的工具,以及它的内存调试器和探针,可以帮助您追踪流浪对象(我经常使用YourKit,但从来不需要调试像这样的问题)。
  • 另外,我不确定子类化会有什么帮助,因为您不控制缓冲区的创建。你说你使用FileChannel.map(),所以你需要以某种方式诱使后者创建你的类的实例,不是吗?
  • 你能转储堆并通过对你的恶意实例的引用进行跟踪吗?
  • @aix:我可以控制(好吧,我是编写调用 FileChannel.map() 的函数的人)控制缓冲区的初始创建,只是我最终使用了它在几十个地方,其中任何一个都可以被 slice()d 并作为私有变量隐藏起来。
  • ByteBuffer 在构建时考虑到了性能,这是理所当然的。

标签: java nio bytebuffer


【解决方案1】:

我正在尝试找出问题文件所在的一个令人讨厌的错误 保持打开状态,因为至少有一个未释放的 ByteBuffer 垃圾回收

这没有意义。未收集的ByteBuffer 不会阻止文件被关闭。你在这里叫错树了。然而,MappedByteBuffer 存在一个众所周知的问题,即 it 永远不会被垃圾收集,从而保持文件有效打开。这确实是一个设计问题:它已为人所知多年,但没有真正的解决方案。道德是不要使用大量的MappedByteBuffers

【讨论】:

  • @Jason S 但是你一直在谈论子类化 ByteBuffers,这不是问题。对它们进行子类化不会有任何区别。它是 Mapped ByteBuffers。即使 MBB 本身被垃圾收集,映射的数据区域也永远不会被释放。而且,正如我所说,没有解决办法。
  • 你没有抓住重点:我不关心我子类化的类;我关心能够用我的仪器类代替另一个类。 (所以如果他们创建了一个 ByteBuffer 接口就不会成为问题)我想创建一个类,我可以将其方法委托给真正的MappedByteBuffer,但拦截slice()duplicate()asReadOnlyBuffer() 所以我也可以装饰这些缓冲区,然后在我的函数需要 ByteBuffer 的地方使用我的类。
  • ...对于它的价值,在我的 PC 上,Java 确实 在 MappedByteBuffer 被垃圾收集时始终释放文件。 (但我使用的是只读模式;如果读写模式有问题不影响我)
  • @JasonS 那么有人或某事阻止你这样做吗? ByteBuffer 是一个抽象类:只需扩展它即可。
【解决方案2】:

JMockit 提供了一种创建模拟类的便捷方式。在这种情况下可能会有所帮助。

模拟你感兴趣的方法,让他们帮你记账,then call the original class' method

【讨论】:

    【解决方案3】:

    我不明白您为什么需要在这里进行子类化。为什么不使用 AOP? AspectJ 似乎非常适合这份工作。如果您真的有野心,请使用Java Instrumentation 并进行一些字节码工程:P

    【讨论】:

      【解决方案4】:

      嗯——我刚刚遇到了这个问题:JavaSpecialists newsletter 168 似乎可以生成一个通用的委托框架——如果由于反射而有点慢。

      Socket 使用策略模式进行实际通信,我们可以指定自己的实现。因此,我们需要做的就是编写我们自己的策略来计算前后流动的字节数。不幸的是,标准策略实现是 java.net.* 包中的包访问,所以我们不能直接使用它们。我们当然不能对它们进行子类化,但我们可以通过反射调用这些方法。但是,由于类本身是包访问,我们需要找到声明的构造函数,将其设置为可访问,然后实例化它。

      【讨论】:

      • 调用包私有方法的主要问题是它们可能会突然改变,也可能是跨 JVM 的不可移植解决方案。一些不变量可能不被尊重。然而,如果你只需要 unmap() 你可以考虑调用清洁器。但是我会解决如何使用 slice() 等跟踪问题。
      • 进行内存转储并找到与问题地址具有相同地址的所有 DirectByteBuffer(s),然后检查谁保留了引用,您将解决问题。
      【解决方案5】:

      我认为你走错了路。 FileChannel.map() 被设计破坏了,因为它限制为 2GB 块,并且您无法控制映射何时应该被垃圾收集器拾取。在 Windows 上,这是应用程序无法再次打开映射文件的常见原因。

      因此,您应该重构代码以摆脱 FileChannel.map(),而不是使用字节码指令或类似的技巧使事情变得更糟。如果您这样做了,下一位必须维护您的代码的程序员将非常感激。 ;-)

      【讨论】:

        猜你喜欢
        • 2018-10-22
        • 2016-08-28
        • 2012-02-27
        • 2011-02-04
        • 2020-07-22
        • 1970-01-01
        • 2019-06-19
        • 2022-12-09
        相关资源
        最近更新 更多