Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With DebugAllocator on, FMLEarlyWindow: The memory address specified is not being tracked #233

Open
BloCamLimb opened this issue Dec 26, 2024 · 4 comments

Comments

@BloCamLimb
Copy link

Description:

Developers may want to track memory leaks. When started with Dorg.lwjgl.util.DebugAllocator=true and earlyWindowProvider = "fmlearlywindow", the game would crash at

try (var glfwImgBuffer = GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)) {

Reason:

MemoryUtil.getAllocator() returns a non-debug allocator (which won't track memory allocation), and StructBuffer.free() via the try-with-resource statement calls to MemoryUtil.nmemFree() which uses the debug allocator. Then org.lwjgl.system.MemoryManage.DebugAllocator.untrack() will throw IllegalStateException which read The memory address specified is not being tracked.

Solution:

Use GLFWImage.malloc(1) instead of GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)

Workaround:

Start with earlyWindowProvider = "dummyprovider".

@shartte
Copy link
Contributor

shartte commented Jan 1, 2025

We might just use try (var glfwImgBuffer = GLFWImage.create(1)) { instead

@BloCamLimb
Copy link
Author

BloCamLimb commented Jan 1, 2025

We might just use try (var glfwImgBuffer = GLFWImage.create(1)) { instead

That would never do. GLFWImage.create uses BufferUtils that cannot be freed explicitly, then it cannot be used in try-with-resources statement.
In addition, there are certain risks in using BufferUtils. For example, you should use it with Reference.reachabilityFence, otherwise it may be released by GC during the execution of the native method, resulting in ACCESS VIOLATION randomly.
But using malloc does not have such a problem, because it must be explicitly freed, while try-with-resources statement ensures it's freed in program order.

@shartte
Copy link
Contributor

shartte commented Jan 1, 2025

The created Buffer holds a strong reference to the ByteBuffer so I am not sure how you draw the conclusion you need any special code to keep the backing memory alive.

@BloCamLimb
Copy link
Author

You are half right, GLFWImage.Buffer holds the ByteBuffer, but the native method won't. For example, if you wrote

ByteBuffer buffer = BufferUtils.createByteBuffer(...); // a direct buffer that is subject to GC
glBufferData(GL_ARRAY_BUFFER, buffer, GL_DYNAMIC_DRAW); // any native method, or glfwSetWindowIcon in your case
// no more strong reference to the buffer

this has a low chance of causing an ACCESS VIOLATION, because the native method only gets the 64-bit address value from the direct buffer, and thus it needs a reachability fence. You can think it's

long addr = memAddress(buffer);
int size = buffer.remaining();
// no more strong reference to the buffer, GC may free the buffer here
// ok, addr may be a dangling pointer
glBufferData(GL_ARRAY_BUFFER, addr, size, GL_DYNAMIC_DRAW);

That's also why all getXX() methods in DirectByteBuffer are similar to the following

public char getChar() {
  try {
    return getChar(ix(nextGetIndex((1 << 1))));
  } finally {
    Reference.reachabilityFence(this);
  }
}

But these are not needed if using MemoryUtil.memAlloc, as the addresses must be explicitly freed. So LWJGL recommends MemoryUtil.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants