【问题标题】:LIfecycle of mapping of native resources in Java (and how to keep them in sync)Java中原生资源映射的生命周期(以及如何保持它们同步)
【发布时间】:2018-03-20 20:45:19
【问题描述】:

请考虑这种情况:

Java 和一些(昂贵的)原生资源之间存在联系,这是映射到 Java 的要求。问题是关于资源的生命周期以及这些资源如何在本机和 Java 上可用,只要它们需要。让我们假设我们可以根据需要在双向都有一个活动的 JNI 连接。

规则如下:

  • 只要包装 Java 对象需要,资源就会保持活动。如果需要,可以对 Java 对象进行垃圾收集。

  • 只要本机部分需要,资源就会保持活动

  • 如果 Java 对象已被 GC 处理,则应该存在一种“重生”该 Java 对象的机制。此 Java 包装器对象可能具有未映射到本机资源的字段,这些字段需要出现在转世对象中。

  • 有一种机制可以通知本机部分 Java 作为客户端不再需要该资源(不保证将来不会再次请求)以及通知本机 Java 已决定不再需要其他客户端的资源。

可能的解决方案列表(以及为什么它们不起作用):

  • 保留包装器的 WeakReference 映射:可能的 java 字段将出现问题,这些字段将被 GC 处理且无法重新创建

  • 使用“finalize”和回收数据:这是不可能的,因为它最多被 GC 调用一次。

  • 使用.clone():它可能被覆盖并且可能丢失数据

  • 使用反射收集所有私有字段并重新应用于新创建的 Java 对象:构造函数未知(可能会产生各种副作用)

  • ... ?

Here is the original (wrong) question。任何想法如何保护这些资源和 Java 之间的连接?

【问题讨论】:

    标签: java garbage-collection jvm resources java-native-interface


    【解决方案1】:

    正确管理资源需要保持一种所有权感(可能是可转让的),其中每个资源的所有者负责在该资源变得不需要时释放该资源。您似乎在描述一个本地资源所有权被掩盖的系统——这是您需要解决的问题。

    听起来您需要某种资源管理器,它本身可能需要 Java 和本机组件。这个:

    • 有一种机制可以通知本机部分,Java 作为客户端,不再需要资源(不保证它 以后不会再要求了)以及通知Java 该本地人已决定没有其他客户想要任何资源 更多。

    让我觉得你可能已经有了其中的一部分,但你的设计似乎让事情变得困难。特别是:

    • 如果 Java 对象已被 GC 处理,则应该存在一种“重生”该 Java 对象的机制。这个 Java 包装器对象可能有字段,而不是 映射到本地资源,这些资源需要出现在 转生对象。

    或多或少意味着不,事实上你不能允许那些Java端对象被GC'd。您需要在 Java 端维护它们的状态,或者至少是其中的一个重要部分。只要您的策略要求,资源管理器需要跟踪它,并防止它被 GC。

    这是设计类似内容的一种方法:

    • 在 Java 端,资源管理器维护本机资源和 Java 端属性之间的关联。可能在客户端使用的包装器和底层原生资源之间存在某种基本数据对象。

    • 根据请求,资源管理器分发主要 Java 端包装对象的实例,客户端通过这些实例访问本机资源和相关的 Java 数据。

    • 资源管理器有一种机制可以找出何时不再需要这些主要的 Java 端对象。这可以通过ReferenceQueue 实现——依赖终结器——但我建议改为使用Closeable,并让用户在使用完它们后负责关闭它们。

    • Java 和资源管理器的本机端合作,根据您选择的任何策略确定何时释放本机资源。最简单的方法是在双方不再有任何客户端时释放它们,但您需要确定这是否与“重生”包装器对象的能力一致。

    听起来您在 Java 和本机端之间需要比目前描述的更丰富的消息集,或者至少需要为您已经拥有的消息提供额外的语义。例如,您可能需要显示“这一侧至少又有一个客户端”的消息。此外,您可能需要消息来协商资源移除,或者至少返回现有消息的值以指示是否应该移除资源。

    另外,请注意确保所有这些线程安全。即使您规定客户端负责以线程安全的方式使用各个包装器,如果资源管理器组件不是线程安全的,您不仅会要求,而且实际上要求麻烦。

    【讨论】:

    • 感谢您定义明确的方法。不幸的是,资源不能被关闭——它不是一个流——并且没有其他方法可以知道 Java 引用是否超出范围。我查看了一个 ReferenceQueue 但我没有找到任何具体信息如何确保没有其他对对象的引用,除了在需要杀死它的时候——在这个例子中,我们想要相反的:保留它活着并且不调用终结器。以及所有与现有引用绑定的示例,例如 Soft/Phantom/Weak,它们都没有涵盖这种情况。
    • @Panayotis,资源不必是Closeable 的流。而且它实际上不必实现Closeable 接口,尽管这对用户来说很方便。关键是你可以让用户负责通过调用一个方法来明确地表明他们已经完成了一个包装器实例。
    • 理论上是的,但是在这种情况下,由于其性质,API 确实无法关闭
    • @Panayotis,您将ReferenceQueues 与软、弱和/或阴影引用一起使用。您知道其中一个已入队,因此不存在比已入队的更强大的引用。
    • 我没有足够的信息来建议细节,但几乎可以肯定,您需要在分发对象时进行一些工作,以便以后能够正确清理它们。当然,在对对象的引用入队后,您不能依赖克隆对象。如果你真的想不出办法,那么也许你应该考虑聘请顾问。
    猜你喜欢
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    • 1970-01-01
    相关资源
    最近更新 更多