Automatic cleanup is one difference, indeed. We cannot add AutoCloseable to the NIO buffer classes, so they can't be used with try-with-resources.
But the major difference is performance:
The MemoryUtil methods call the native memory allocator and each allocation & deallocation has considerable overhead, depending on the implementation. LWJGL provides bindings to 3 allocators: system default, jemalloc (faster on Linux & macOS), rpmalloc (fastest, but requires per-thread setup).
The MemoryStack is pure Java code that returns slices out of a pre-allocated, thread-local, fixed-size memory block. A stack allocation simply increments a pointer, with proper alignment, and returns a buffer object. Popping the stack is basically two instructions (happens automatically at the end of the try-with-resources block). The drawback? The stack size cannot grow (defaults to 64kb, can be statically changed with Configuration.STACK_SIZE), so it's only good for small, short-lived buffers (which are very common actually).
Read the
Memory FAQ for more information.