游戏因回调失败而崩溃,因为闭包实例已被垃圾回收

Game crash with Callback failed because the closure instance has been garbage collected

我的小游戏有问题。我是 LJWGL3 的新手(使用了很长时间的 2),想实现一个在 window 和全屏模式之间切换的键。我已经可以毫无问题地做到这一点。但是现在如果我经常按这个键,我的游戏就会崩溃。有时更晚或更早。没有定时崩溃。我尝试了很多(3 小时),现在 运行 没有想法。有人知道我做错了什么吗?我无法 post 完整的源代码,因为它太多了。 我 post 把代码的一些重要部分放在这里。 首先是错误:

Exception in thread "main" org.lwjgl.system.libffi.ClosureError: Callback failed because the closure instance has been garbage collected.
at org.lwjgl.system.JNI.invokeIIPPPP(Native Method)
at org.lwjgl.glfw.GLFW.nglfwCreateWindow(GLFW.java:1146)
at org.lwjgl.glfw.GLFW.glfwCreateWindow(GLFW.java:1227)
at com.dungeon.gl.FullscreenCreation.setFullScreen(FullscreenCreation.java:63)
at com.dungeon.gl.GLAction.updateGL(GLAction.java:78)
at com.dungeon.MainAction.start(MainAction.java:69)
at com.dungeon.MainAction.main(MainAction.java:21)

这是我用来检查 KEY 是否按下的代码:

    if(glfwGetKey(appID, GLFW_KEY_F) == GLFW_PRESS) {
        FullscreenCreation.setFullScreen(!fullscreen);
    } 

这是我的 FullscreenCreation.java

    public static void setFullScreen(boolean fullScreen) {

    //i don't have copy the variables like fullscreen to here
    if (fullscreen == fullScreen)
        return;
    fullscreen = fullScreen; 

    if (fullScreen) {
        windowWIDTH = WIDTH;
        windowHEIGHT = HEIGHT;

        //get monitor resolution
        ByteBuffer vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        WIDTH = GLFWvidmode.width(vidMode);
        HEIGHT = GLFWvidmode.height(vidMode);
    } else {
        WIDTH = windowWIDTH;
        HEIGHT = windowHEIGHT;
    }       

    //create new window THIS IS THE POINT WHERE ITS CRASH AFTER A WHILE
    long display = glfwCreateWindow(WIDTH, HEIGHT, MainAction.BASIC_WINDOW_TITLE, fullScreen ? glfwGetPrimaryMonitor() : 0, appID);
    glfwDestroyWindow(appID); //kill the window (for now)

    //add new callback for resizing the window
    glfwSetCallback(display, GLFWWindowSizeCallback(new SAM() {
        @Override
        public void invoke(long window, int width, int height) {
            if(width!=0 && height!=0) {
            WIDTH=width;
            HEIGHT=height;
            isResize=true;
            }
        }
    }));


    //now apply the new fullscreen window
    appID = display;

    //some GL stuff for the new window
    glfwMakeContextCurrent(appID);        
    GL.createCapabilities();

    glfwSwapInterval(0); //disable vsync

    //only call glEnable(GL_TEXTURE_2D), GL_BLEND ...
    DisplayCreation.enableGLStuff();

    isResize = true;
}

如果您需要更多代码,我会 post 更多。

您需要对您创建的每个回调保持强引用,因为垃圾收集器不知道它在本机代码中使用,因此会以其他方式收集它。

所以这意味着您必须将回调对象存储在一个字段中。

private static GLFWWindowSizeCallback sizeCallback;

//...

public static void setFullScreen(boolean fullScreen) {
    //...
    sizeCallback = GLFWWindowSizeCallback(new SAM() {
        //...
    };
    glfwSetCallback(display, sizeCallback);
    //...
}

然后,当您使用完回调后(即当您销毁 window 时),您只需释放它即可。

sizeCallback.release();

注意:要释放给定window的所有回调,也可以使用Callbacks.glfwReleaseCallbacks.


关于 LWJGL 2 和 3 之间的差异,您可以查看 migration guide,其中也包含对此问题的简要说明。