如果允许JVM在垃圾收集期间移动堆内存,那么垃圾收集如何不因移动指针而导致JNI爆炸?

Posted

技术标签:

【中文标题】如果允许JVM在垃圾收集期间移动堆内存,那么垃圾收集如何不因移动指针而导致JNI爆炸?【英文标题】:If JVM is allowed to move heap memory during garbage collect then how does garbage collection not cause JNI to explode due to moving pointers? 【发布时间】:2016-02-16 20:49:15 【问题描述】:

使用 ByteBuffer 就是一个例子;将数据从 Array 或 HeapByteBuffer 复制到 DirectByteBuffer 会执行 JNI 调用。

基本上这个...

public static void copyFromByteArray(byte[] src, long srcPos, long dstAddr, long length)

    long offset = arrayBaseOffset + srcPos;
    while (length > 0)
    
        long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length;
        unsafe.copyMemory(src, offset, null, dstAddr, size);
        length -= size;
        offset += size;
        dstAddr += size;
    

它使用了计算堆指针并直接从堆中复制的魔法。如果该指针在此例程中变得无效,这怎么可能是远程安全的?

对于相关人员:UNSAFE_COPY_THRESHOLD 为 1GB

编辑:感谢您的回复。我正在为寻找同一问题答案的任何人添加以下参考。这个过程使用了安全点(我不知道他们叫什么,所以我不能只是谷歌它)。

参考文献:

http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html http://chriskirk.blogspot.com/2013/09/what-is-java-safepoint.html

【问题讨论】:

【参考方案1】:

GC 必须强制所有线程进入安全点以移动对象。这意味着所有线程都在已知状态下挂起,以便可以安全地更新所有引用。

在这种情况下,不安全操作是编译器固有的,这意味着您实际上并没有进行调用,而是 VM 插入了实现该功能的机器代码。由于该位置不是安全点,因此该位置可能不会发生 GC。如果需要进行 GC,则 GC 将被推迟,直到执行不安全操作的线程完成此操作并达到这样的安全点。

【讨论】:

unsafe.copyMemory 是一个 JNI 调用。 C 代码不安全且无锁。它通过将对象转换为 (uintptr_t) 并直接从本机内存地址复制来工作。这是否意味着所有 JNI 命令 = 不安全状态?我正在努力更好地了解我可以摆脱什么。 不回答问题。您可以强制将 JNI 方法放入您喜欢的所有安全点,但之后它仍将具有未更改值的直接对象指针。这个答案不能确定正确的机制。【参考方案2】:

所有传递给 JNI 方法或由 JNI 方法获取或创建的 Java 对象都将被冻结以用于 GC,直到该方法返回。这就是他们数量有限的原因。

【讨论】:

我关心的是java对象的内存地址,而不是指针。 (基本上是你能做的最不安全的事情)

以上是关于如果允许JVM在垃圾收集期间移动堆内存,那么垃圾收集如何不因移动指针而导致JNI爆炸?的主要内容,如果未能解决你的问题,请参考以下文章

jvm 垃圾收集器

JVM之垃圾收集器

JVM堆内存控制/分代垃圾回收

深入理解JVM——垃圾收集策略具体解释

JVM-垃圾收集器

JVM 垃圾回收笔记