JDK-8350375 : Malloc/free fails on Mac
  • Type: Bug
  • Component: core-libs
  • Affected Version: 25
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2025-02-19
  • Updated: 2025-02-21
  • Resolved: 2025-02-21
Related Reports
Duplicate :  
Description
Using `UNSAFE.allocateMemory()` and `                                UNSAFE.freeMemory()` in certain ways causes the VM to crash.

This issue was only observed on macosx-aarch64 and macosx-x64 and not on Linux and Windows.

Reproduce (see code further below):

 make test TEST=open/test/jdk/java/foreign/TestCarrierLocalArenaPoolsStress.java JTREG_REPEAT_COUNT=1000

...
Repeating Jtreg run: 64 out of 1000
...

STARTED    TestCarrierLocalArenaPoolsStress::stress 'stress()'
STDOUT:
PT:   0:000007208 EXPANDING VT FJP
PT:   0:005320166 DONE EXPANDING
PT:   0:005854708 CREATING 1024 THREADS USING java.lang.ThreadBuilders$VirtualThreadBuilder@1581572f
PT:   0:011715500 SLEEPING
PT:  10:019071666 INTERRUPTING
PT:  10:020941708 DONE INTERRUPTING
PT:  10:036864250 ALL THREADS COMPLETED
PT:  10:037108583 CREATING 32 THREADS USING java.lang.ThreadBuilders$PlatformThreadBuilder@465bb39d
PT:  10:161722958 SLEEPING

TEST RESULT: Error. Agent communication error: java.io.EOFException; check console log for any additional details

Code:

```
final class TestCarrierLocalArenaPoolsStress {

    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
    private static final long POOL_SIZE = 64;
    private static final VarHandle LONG_HANDLE = JAVA_LONG.varHandle();

    /**
     * The objective of this test is to try to provoke a situation where threads are
     * competing to use allocated pooled memory and then trying to make sure no thread
     * can see the same shared memory another thread is using.
     */
    @Test
    void stress() throws InterruptedException {
        final long begin = System.nanoTime();

        System.out.println(duration(begin) + "EXPANDING VT FJP");

        // Encourage the VT ForkJoin pool to expand/contract so that VT:s will be allocated
        // on FJP threads that are later terminated.
        LongStream.range(0, Runtime.getRuntime().availableProcessors() * 2L)
                .parallel()
                // Using a CompletableFuture expands the FJP
                .forEach(_ -> Thread.ofVirtual().start(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException ie) {
                        throw new RuntimeException(ie);
                    }
                }));

        System.out.println(duration(begin) + "DONE EXPANDING");

        // Make sure it works for both virtual and platform threads
        for (var threadBuilder : List.of(Thread.ofVirtual(), Thread.ofPlatform())) {
            final int noThreads = threadBuilder instanceof Thread.Builder.OfVirtual ? 1024 : 32;
            System.out.println(duration(begin) + "CREATING " + noThreads + " THREADS USING " + threadBuilder);
            final Thread[] threads = IntStream.range(0, noThreads).mapToObj(_ ->
                    threadBuilder.start(() -> {
                        final long threadId = Thread.currentThread().threadId();
                        while (!Thread.interrupted()) {
                            for (int i = 0; i < 1_000_000; i++) {
                                    // Try to assert no two threads get allocated the same memory region.
                                final long adr = UNSAFE.allocateMemory(POOL_SIZE);
                                UNSAFE.putLongVolatile(null, adr, threadId);
                                long v = UNSAFE.getLongVolatile(null, adr);
                                assertEquals(threadId, v);
                                UNSAFE.freeMemory(adr);
                                //}
                            }
                            Thread.yield(); // make sure the driver thread gets a chance.
                        }
                    })).toArray(Thread[]::new);
            System.out.println(duration(begin) + "SLEEPING");
            Thread.sleep(Duration.of(10, SECONDS));
            System.out.println(duration(begin) + "INTERRUPTING");
            Arrays.stream(threads).forEach(
                    thread -> {
                        assertTrue(thread.isAlive());
                        thread.interrupt();
                    });
            System.out.println(duration(begin) + "DONE INTERRUPTING");

            // VTs are daemon threads ...
            Arrays.stream(threads).forEach(t -> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println(duration(begin) + "ALL THREADS COMPLETED");
        }
        System.out.println(duration(begin) + "DONE");
    }
}

    private static String duration(Long begin) {
        var duration = Duration.of(System.nanoTime() - begin, ChronoUnit.NANOS);
        long seconds = duration.toSeconds();
        int nanos = duration.toNanosPart();
        return (Thread.currentThread().isVirtual() ? "VT: " : "PT: ") +
                String.format("%3d:%09d ", seconds, nanos);
    }

```



Comments
The issue title should mention Unsafe, just mentioning malloc/free is too ambiguous.
20-02-2025