MemoryStack#stackMalloc 和 MemoryStack stack = stackPush(); 有什么区别? stack.malloc?

What is the difference in MemoryStack#stackMalloc and MemoryStack stack = stackPush(); stack.malloc?

我只是想知道 MemoryStack#stackMalloc 和其他静态 stackXx 方法与 try (var stack = stackPush()) { stack.malloc } 模式有什么区别,更值得鼓励的是什么?

我想他们也一样。只有 stackPush 你可以有多个堆栈?

static 方法MemoryStack.stackMalloc(int), MemoryStack.stackCalloc(int) (as well as their specializations returning IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer or LWJGL 3's PointerBuffer) first call MemoryStack.stackGet() to retrieve (and potentially initialize) the Thread-Local variable TLS (in the current implementation) to retrieve a MemoryStack instance for the current thread and then call the instance methods MemoryStack.malloc(int), MemoryStack.calloc(int) 或该线程本地 MemoryStack 实例上的任何类型特化。

重复执行此操作将慢慢导致当前线程的 MemoryStack 实例中的“堆栈”内存耗尽,进一步调用会引发 OutOfMemoryError Java 异常。 所以,如果你 only 曾经使用静态 malloc/calloc(int) 方法分配内存,并且不做任何其他事情,比如通过重置当前(也是唯一的)堆栈帧的堆栈指针实例方法 MemoryStack.setPointer(int) 来“回收”为该 MemoryStack 分配的内存,然后该 MemoryStack 实例很快就会 运行 内存不足。

创建新“堆栈框架”的静态方法MemoryStack.stackPush() also does a Thread-Local lookup of the calling thread's MemoryStack instance (with potential initialization if not yet initialized for the current thread) followed by a call to the instance method MemoryStack.push()

来自该 MemoryStack.push() 方法的 Java 文档:

Stores the current stack pointer and pushes a new frame to the stack. This method should be called when entering a method, before doing any stack allocations. When exiting a method, call the pop() method to restore the previous stack frame.

Pairs of push/pop calls may be nested. Care must be taken to:

  • match every push with a pop
  • not call pop before push has been called at least once
  • not nest push calls to more than the maximum supported depth

因此,我们看到 MemoryStack.push() 将创建一个新的“堆栈框架”,它基本上就是当前指针的位置。接下来的 MemoryStack.pop() 会将当前堆栈帧从帧的 list/stack 中弹出,并将 MemoryStack 的指针重置为先前的位置(它在 push() 之前的位置)被称为)。

现在,MemoryStack class 的设计使得它可以与 try-with-resources Java statement (which itself has been introduced with Java 7) by implementing the java.lang.AutoCloseable interface. The implementation of the close() 方法一起使用,因为 MemoryStack 只需按顺序在 this MemoryStack 实例上执行 pop()弹出当前“堆栈帧”并将 MemoryStack 的指针重置为 previous/parent 堆栈帧(可能是 first/default 帧)的值。

所以,您想要比较的不同方法确实有不同的作用:

  1. MemoryStack.malloc(int) 没有 push/pop 的 MemoryStack 将增加 MemoryStack 的指针
  2. MemoryStack.stackPush() 作为 try-with-resources 语句中的资源将创建一个新的“堆栈框架”,以便相应的 MemoryStack.pop() 调用可以“回收”分配的内存(即差异指向前一帧的指针)

本质上,您应该始终 push()、分配内存然后 pop(),以便在您的应用程序中找到合适的分配“框架”。您可以使用 try-with-resources Java 语句(或手动执行)来做到这一点。

此外,我想向您推荐有关 "Memory Management in LWJGL" 的 LWJGL 3 博客文章,其中包含有关不同分配策略及其在某些情况下的优缺点的更多有用信息。

最后,来自你post的问题:

Only that with stackPush you can have multiple stacks?

可以这样回答,是的,您可以通过以下任一方式拥有不同的 MemoryStack 实例:

  • 自己分配那些 MemoryStack 实例(没有什么可以阻止您调用 MemoryStack.create() 或其重载之一
  • 使用多个线程,当使用静态 MemoryStack 方法时每个线程将获得自己的 MemoryStack 实例,该方法将在 MemoryStack.stackGet() 获得的 Thread-Local 实例上运行。

但是,您可能真的不需要每个线程有多个 MemoryStack 实例,因为根据 MemoryStack 的“性质”,它应该或多或少类似于您的 Java 方法调用的调用堆栈,这根据定义,每个线程都会发生。

感谢您的回答。原来如此,没想到会运行变成OutOfMemoryError with the static stackMalloc.

import org.junit.jupiter.api.Test
import org.lwjgl.system.MemoryStack

/**
 * @see MemoryStack
 * @author Erwin Müller, {@code <erwin@muellerpublic.de>}
 */
class MemoryStackTest {

    // works only 64 times until OutOfMemoryError
    @Test
    void "static stackMalloc OutOfMemory"() {
        for (int i = 0; i < 10000; i++) {
            println "[stackMalloc] $i"
            MemoryStack.stackMalloc(1024)
        }
    }

    @Test
    void "push pop malloc OutOfMemory"() {
        for (int i = 0; i < 10000; i++) {
            MemoryStack.stackPush().withCloseable {
                println "[push-Malloc] $i"
                it.malloc(1024)
            }
        }
    }
}