java-native-access/jna · critical · OutOfMemoryError

Cannot allocate <size> bytes

Error message

Cannot allocate <size> bytes

What it means

The Memory constructor throws this OutOfMemoryError when the underlying native malloc returns NULL (peer == 0), i.e. the OS/native allocator could not provide `size` bytes. Unlike a Java heap OOM, this reflects native heap exhaustion.

Source

Thrown at src/com/sun/jna/Memory.java:119

        @Override
        public String toString() {
            return super.toString() + " (shared from " + Memory.this.toString() + ")";
        }
    }

    /**
     * Allocate space in the native heap via a call to C's <code>malloc</code>.
     *
     * @param size number of <em>bytes</em> of space to allocate
     */
    public Memory(long size) {
        this.size = size;
        if (size <= 0) {
            throw new IllegalArgumentException("Allocation size must be greater than zero");
        }
        peer = malloc(size);
        if (peer == 0)
            throw new OutOfMemoryError("Cannot allocate " + size + " bytes");

        allocatedMemory.put(peer, new WeakReference<>(this));
        cleanable = Cleaner.getCleaner().register(this, new MemoryDisposer(peer));
    }

    protected Memory() {
        super();
        cleanable = null;
    }

    /** Provide a view of this memory using the given offset as the base address.  The
     * returned {@link Pointer} will have a size equal to that of the original
     * minus the offset.
     * @throws IndexOutOfBoundsException if the requested memory is outside
     * the allocated bounds.
     */
    @Override
    public Pointer share(long offset) {

View on GitHub (pinned to d036ad9781)

Solutions

  1. Reduce the requested allocation size or allocate in chunks.
  2. Check process/container memory limits (ulimit, cgroups) and raise them.
  3. Free Memory deterministically (close()/dispose or rely on explicit cleanup) instead of holding many allocations.
  4. Switch to a 64-bit JVM/address space if on 32-bit.
  5. Profile for native leaks; failing malloc after long runs usually means leaked native memory.

Example fix

// before
Memory buf = new Memory(totalSize); // totalSize ~ 4GB, malloc fails
// after
for (Chunk c : chunks) { Memory buf = new Memory(c.size); /* process and release */ }
Defensive patterns

Strategy: fallback

Validate before calling

static boolean sizePlausible(long bytes, long maxBytes) { return bytes > 0 && bytes <= maxBytes; }

Try / catch

Memory m;
try {
  m = new Memory(size);
} catch (OutOfMemoryError e) {
  if (e.getMessage().startsWith("Cannot allocate ")) {
    m = allocateInChunks(size); // fall back to chunked allocation
  } else throw e;
}

Prevention

When it happens

Trigger: new Memory(hugeSize) where the native heap cannot satisfy the request; cumulative native leaks exhausting the process address space; allocating on 32-bit JVMs beyond ~2-4GB addressable space.

Common situations: Allocating buffers sized from untrusted/oversized input; 32-bit processes; many live Memory instances never released (relying on Cleaner) causing native fragmentation/exhaustion; container memory limits making malloc fail.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/32ac732eb2f857b1. Report an issue: GitHub.