Skip to content

Size the translator heap from the machine so large apps build unmodified - #5521

Merged
shai-almog merged 6 commits into
masterfrom
fix/translator-heap-large-apps
Aug 5, 2026
Merged

Size the translator heap from the machine so large apps build unmodified#5521
shai-almog merged 6 commits into
masterfrom
fix/translator-heap-large-apps

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Fixes #5511.

The problem

The reported -Xmx512m is not an isolated lowball. Every ByteCodeTranslator fork carried a hard-coded heap, tuned years ago against the sample apps:

Target Before
JavaScript 512m
iOS 1024m
Windows native 2g
Linux native 2g

A large app exhausts that partway through translation and dies with OutOfMemoryError. The only way out was to discover CN1_TRANSLATOR_OPTS by reading this plugin's source, which is exactly what the reporter had to do.

The defaults were tighter than they looked. Bisecting the heap on the in-repo hellocodenameone JS build, the translator OOMs at 384m and needs 448m to finish. That is under 1.5x headroom on the sample app.

The fix

TranslatorHeap derives the ceiling from the machine: half the detected memory budget, capped at 4g, floored at each target's historical constant so no target can end up with less heap than before. The budget reads the cgroup limit before falling back to physical RAM, so a build inside a container sizes against the container's limit rather than the host's. That is what keeps an over-large app failing as a Java OutOfMemoryError we can report, instead of a kernel OOM-kill we cannot.

Raising the ceiling is close to free for ordinary apps: -Xmx is a reservation, not a commitment.

CN1_TRANSLATOR_MAX_HEAP_MB pins the value per box. It may sit above the 4g cap (that cap bounds only the automatic choice; clamping the knob would achieve nothing since an operator would just switch to CN1_TRANSLATOR_OPTS=-Xmx, which no ceiling applies to). It may not exceed the memory the machine actually has -- that is the setting that OOM-kills a build box rather than failing one build, and it is the likely shape of a typo.

Also in scope

  • A translator OOM now says so. It previously returned false and the build reported nothing more useful than a failed step. The JS and iOS builders recognise the signature in the captured output and name the heap in use and how to raise it.
  • Local iOS translator timeout 420s -> 600s, matching the cloud builder running the identical translator over identical input. Translation time grows with app size, so a large app could translate fine on the build server yet be killed on the developer's own machine, which is usually the slower of the two.
  • JavascriptBundleWriter peak reduction. It held the whole bundle twice (as StringBuilders and again as Strings) at the chunk handoff, and kept every chunk resident until the last was written. Chunks are now freed as they materialise and as they reach disk.

Measured

Bundle shape Before After
Single chunk (hello-world) 448m 448m
Two chunks (rta.off) 416m 384m

The 32MB reduction is about one full UTF-16 copy of the emitted JS. It scales with bundle size, so it does nothing for a single-chunk app and progressively more for a large one -- the emit change is a modest structural win; the heap sizing is what actually unblocks the reported app.

The emitted bundle is byte-identical, verified on both the single-chunk and multi-chunk paths (the first check alone would not have covered the lead-chunk write loop the change touches).

Verification

  • Plugin: 435 tests green, SpotBugs 0 findings
  • ByteCodeTranslator: tests green, SpotBugs 0 findings
  • 13 unit tests pin the heap policy, including that no target can be handed less heap than its old constant

Companion change

The cloud side needs the matching fix and is a separate PR against BuildDaemon: the daemon carried the same 512m for JS and, on iOS, no CN1_TRANSLATOR_OPTS pass-through at all -- so a cloud customer, who cannot set env vars on the build box, had no escape hatch whatsoever.

Not addressed

Android remains a fixed cliff: org.gradle.jvmargs=-Xmx2048m and dexOptions javaMaxHeapSize "3g" are hard-coded with no build-hint override. That is a separate subsystem from the translator, but it is where a very large app hits the next wall.

🤖 Generated with Claude Code

Every ByteCodeTranslator fork carried a hard-coded -Xmx tuned years ago
against the sample apps: 512m for JavaScript, 1024m for iOS, 2g for the
native Windows/Linux targets. A genuinely large app exhausts that partway
through translation and dies with OutOfMemoryError, and the only way out
was to discover CN1_TRANSLATOR_OPTS by reading this plugin's source
(issue #5511).

The defaults were tighter than they looked. Bisecting the heap on the
in-repo hellocodenameone JS build, the translator OOMs at 384m and needs
448m to finish -- under 1.5x headroom on the sample app itself.

TranslatorHeap now derives the ceiling from the machine: half the detected
memory budget, capped at 4g, and floored at each target's historical
constant so no target can end up with less heap than before. The budget
reads the cgroup limit before falling back to physical RAM, so a build
running inside a container sizes against the container's limit rather than
the host's -- which is what keeps an over-large app failing as a Java
OutOfMemoryError we can report instead of a kernel OOM-kill we cannot.
Raising the ceiling is close to free for ordinary apps because -Xmx is a
reservation, not a commitment.

CN1_TRANSLATOR_MAX_HEAP_MB pins the value per box. It may sit above the 4g
cap, which only bounds the value we pick automatically -- clamping the knob
would achieve nothing, since an operator would just switch to
CN1_TRANSLATOR_OPTS=-Xmx, which no ceiling applies to. It may not exceed
the memory the machine actually has: that is the setting that OOM-kills a
build box rather than failing one build, and it is the likely shape of a
typo.

Alongside that, two things the translator failure path was missing:

  * A translator that died for lack of memory previously returned false and
    the build reported nothing more useful than a failed step. Both the JS
    and iOS builders now recognise the out-of-memory signature in the
    captured output and say so, naming the heap in use and how to raise it.

  * The local iOS translator ran under a 420s timeout while the cloud
    builder runs the identical translator over identical input at 600s.
    Translation time grows with app size, so a large app could translate
    fine on the build server yet be killed mid-run on the developer's own
    machine, which is usually the slower of the two. Both are 600s now.

JavascriptBundleWriter also held the whole bundle twice -- as StringBuilders
and again as Strings -- at the point it materialises the chunks, and kept
every chunk resident until the last one had been written. Chunks are now
released as they materialise and as they reach disk. On a two-chunk bundle
that drops the minimum viable heap from 416m to 384m, about one full UTF-16
copy of the emitted JS; the saving scales with bundle size, so it does
nothing for a single-chunk app and progressively more for a large one. The
emitted bundle is byte-identical, verified on both the single-chunk and
multi-chunk paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 5, 2026 02:37
check-copyright-headers rejects any added source file without the complete
Codename One GPLv2 + Classpath Exception header; the new TranslatorHeapTest
went up without one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fac0ce6fe7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the reliability of large-app builds by replacing per-target hard-coded ByteCodeTranslator heaps with an automatically sized heap derived from the machine/container memory budget (with per-target floors and a configurable override). It also improves diagnostics for translator out-of-memory failures, adjusts the local iOS translator timeout to match cloud behavior, and reduces peak memory during JavaScript bundle emission.

Changes:

  • Introduce TranslatorHeap to auto-size -Xmx from cgroup/physical RAM with floors, a 4GB auto cap, and CN1_TRANSLATOR_MAX_HEAP_MB override.
  • Improve JS/iOS builder error reporting by detecting translator OOM signatures and emitting actionable advice; increase local iOS translator timeout to 600s.
  • Reduce JavaScript bundle writer peak by releasing chunk builders/strings as soon as they materialize and are written.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vm/ByteCodeTranslator/src/com/codename1/tools/translator/JavascriptBundleWriter.java Releases JS chunk builders/strings earlier to reduce peak memory during bundle emission.
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java New heap sizing policy: cgroup-aware detection, floors, cap, and environment override; includes OOM signature/advice helpers.
maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/TranslatorHeapTest.java Adds unit tests to pin heap sizing and OOM-advice behavior.
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/JavaScriptBuilder.java Switches JS translator heap to auto-sized value and adds OOM failure recognition/advice.
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java Switches iOS translator heap to auto-sized value, increases timeout to 600s, and adds OOM recognition/advice.
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/WindowsNativeBuilder.java Replaces fixed -Xmx2g with TranslatorHeap sizing floored at 2GB.
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/LinuxNativeBuilder.java Replaces fixed -Xmx2g with TranslatorHeap sizing floored at 2GB.
maven/codenameone-maven-plugin/spotbugs-exclude.xml Adds SpotBugs exclusion for fixed Linux cgroup limit probe paths in TranslatorHeap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings August 5, 2026 02:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (4)

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/TranslatorHeapTest.java:142

  • This test hard-fails if detectBudgetMB() returns -1 on the current runtime/container. Since the implementation is explicitly designed to gracefully fall back to historical constants when detection is unavailable, this assertion can make the test suite flaky across CI environments/JVMs without indicating a functional regression.
    @Test
    public void detectsSomeMemoryBudgetOnThisMachine() {
        // The whole policy degrades to the old constants if this returns -1, so
        // make sure the detection actually works on a normal build machine.
        assertTrue(TranslatorHeap.detectBudgetMB() > 0);

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java:213

  • cgroupLimitMB() reads fixed paths under /sys/fs/cgroup, which only reflect the root cgroup. If the Maven process runs in a nested cgroup with a memory limit (e.g. systemd slice scopes, some CI setups), these files can be unlimited while the process’ own cgroup directory is limited. That can cause the heap auto-sizing to overshoot the real limit and get OOM-killed, undermining the goal of sizing to the container budget.
    private static long cgroupLimitMB() {
        long v2 = readLimitFile(new File("/sys/fs/cgroup/memory.max"));
        if (v2 > 0) {
            return v2;
        }
        return readLimitFile(new File("/sys/fs/cgroup/memory/memory.limit_in_bytes"));
    }

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java:166

  • outOfMemoryAdvice() says the machine’s “memory budget” doesn’t allow a larger heap, but the OOM may also occur because of the policy ceiling (or an explicit floor) even when the machine has plenty of RAM. This wording can mislead users into thinking their hardware/container limit is the blocker when it may just be the default cap/config.
    static String outOfMemoryAdvice(int heapMB, boolean canConfigureEnvironment) {
        String head = "The ByteCodeTranslator ran out of memory (it was given -Xmx" + heapMB + "m). "
                + "This app needs a larger translation heap than the build machine's memory budget allows. ";
        if (canConfigureEnvironment) {

vm/ByteCodeTranslator/src/com/codename1/tools/translator/JavascriptBundleWriter.java:237

  • Nulling out the chunks list entries won’t actually release the last (often largest) StringBuilder early because the local tail variable still holds a strong reference to it until the method returns. This partially defeats the intended peak-memory reduction during the subsequent minify/hoist passes.
        for (int i = 0; i < chunks.size(); i++) {
            // Drop each builder as it materialises. Holding the whole bundle
            // simultaneously as StringBuilders and as Strings doubles the
            // translator's peak for no benefit, and on a large app that
            // doubling is the difference between finishing and dying with
            // OutOfMemoryError (issue #5511).
            chunkStrings.add(chunks.get(i).toString());
            chunks.set(i, null);

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 417 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 21478 ms

  • Hotspots (Top 20 sampled methods):

    • 17.71% java.util.ArrayList.indexOf (327 samples)
    • 6.93% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (128 samples)
    • 4.39% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (81 samples)
    • 3.68% com.codename1.tools.translator.BytecodeMethod.optimize (68 samples)
    • 3.52% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (65 samples)
    • 3.14% java.lang.StringBuilder.append (58 samples)
    • 2.98% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (55 samples)
    • 2.49% org.objectweb.asm.tree.analysis.Analyzer.analyze (46 samples)
    • 2.11% com.codename1.tools.translator.Parser.classIndex (39 samples)
    • 2.00% java.util.HashMap.hash (37 samples)
    • 1.68% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (31 samples)
    • 1.57% com.codename1.tools.translator.BytecodeMethod.equals (29 samples)
    • 1.57% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (29 samples)
    • 1.35% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (25 samples)
    • 1.19% java.lang.System.identityHashCode (22 samples)
    • 1.19% java.lang.String.equals (22 samples)
    • 1.08% java.lang.Object.hashCode (20 samples)
    • 1.03% org.objectweb.asm.ClassReader.readCode (19 samples)
    • 0.87% com.codename1.tools.translator.BytecodeMethod.appendMethodC (16 samples)
    • 0.81% com.codename1.tools.translator.Parser.cullMethods (15 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 7.87% (7618/96846 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.73% (39663/513151), branch 2.83% (1375/48635), complexity 3.16% (1651/52181), method 4.87% (1345/27595), class 9.98% (367/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 7.87% (7618/96846 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.73% (39663/513151), branch 2.83% (1375/48635), complexity 3.16% (1651/52181), method 4.87% (1345/27595), class 9.98% (367/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 126ms / native 94ms = 1.3x speedup
SIMD float-mul (64K x300) java 117ms / native 114ms = 1.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 73.000 ms
Base64 CN1 decode 89.000 ms
Base64 native encode 372.000 ms
Base64 encode ratio (CN1/native) 0.196x (80.4% faster)
Base64 native decode 282.000 ms
Base64 decode ratio (CN1/native) 0.316x (68.4% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 63ms / native 4ms = 15.7x speedup
SIMD float-mul (64K x300) java 74ms / native 4ms = 18.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 168.000 ms
Base64 CN1 decode 116.000 ms
Base64 SIMD encode 102.000 ms
Base64 encode ratio (SIMD/CN1) 0.607x (39.3% faster)
Base64 SIMD decode 87.000 ms
Base64 decode ratio (SIMD/CN1) 0.750x (25.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 27.000 ms
Image createMask (SIMD on) 20.000 ms
Image createMask ratio (SIMD on/off) 0.741x (25.9% faster)
Image applyMask (SIMD off) 46.000 ms
Image applyMask (SIMD on) 42.000 ms
Image applyMask ratio (SIMD on/off) 0.913x (8.7% faster)
Image modifyAlpha (SIMD off) 47.000 ms
Image modifyAlpha (SIMD on) 180.000 ms
Image modifyAlpha ratio (SIMD on/off) 3.830x (283.0% slower)
Image modifyAlpha removeColor (SIMD off) 41.000 ms
Image modifyAlpha removeColor (SIMD on) 31.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.756x (24.4% faster)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 66ms / native 5ms = 13.2x speedup
SIMD float-mul (64K x300) java 65ms / native 5ms = 13.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 197.000 ms
Base64 CN1 decode 140.000 ms
Base64 SIMD encode 102.000 ms
Base64 encode ratio (SIMD/CN1) 0.518x (48.2% faster)
Base64 SIMD decode 101.000 ms
Base64 decode ratio (SIMD/CN1) 0.721x (27.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 27.000 ms
Image createMask (SIMD on) 23.000 ms
Image createMask ratio (SIMD on/off) 0.852x (14.8% faster)
Image applyMask (SIMD off) 60.000 ms
Image applyMask (SIMD on) 58.000 ms
Image applyMask ratio (SIMD on/off) 0.967x (3.3% faster)
Image modifyAlpha (SIMD off) 194.000 ms
Image modifyAlpha (SIMD on) 56.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.289x (71.1% faster)
Image modifyAlpha removeColor (SIMD off) 71.000 ms
Image modifyAlpha removeColor (SIMD on) 59.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.831x (16.9% faster)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300) java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 246.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 63.000 ms
Base64 encode ratio (SIMD/CN1) 0.256x (74.4% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.492x (50.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 13.000 ms
Image createMask (SIMD on) 7.000 ms
Image createMask ratio (SIMD on/off) 0.538x (46.2% faster)
Image applyMask (SIMD off) 25.000 ms
Image applyMask (SIMD on) 19.000 ms
Image applyMask ratio (SIMD on/off) 0.760x (24.0% faster)
Image modifyAlpha (SIMD off) 16.000 ms
Image modifyAlpha (SIMD on) 11.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.688x (31.3% faster)
Image modifyAlpha removeColor (SIMD off) 20.000 ms
Image modifyAlpha removeColor (SIMD on) 12.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.600x (40.0% faster)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 80ms / native 4ms = 20.0x speedup
SIMD float-mul (64K x300) java 124ms / native 3ms = 41.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 157.000 ms
Base64 CN1 decode 93.000 ms
Base64 native encode 649.000 ms
Base64 encode ratio (CN1/native) 0.242x (75.8% faster)
Base64 native decode 297.000 ms
Base64 decode ratio (CN1/native) 0.313x (68.7% faster)
Base64 SIMD encode 47.000 ms
Base64 encode ratio (SIMD/CN1) 0.299x (70.1% faster)
Base64 SIMD decode 44.000 ms
Base64 decode ratio (SIMD/CN1) 0.473x (52.7% faster)
Base64 encode ratio (SIMD/native) 0.072x (92.8% faster)
Base64 decode ratio (SIMD/native) 0.148x (85.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 9.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.444x (55.6% faster)
Image applyMask (SIMD off) 63.000 ms
Image applyMask (SIMD on) 44.000 ms
Image applyMask ratio (SIMD on/off) 0.698x (30.2% faster)
Image modifyAlpha (SIMD off) 48.000 ms
Image modifyAlpha (SIMD on) 41.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.854x (14.6% faster)
Image modifyAlpha removeColor (SIMD off) 55.000 ms
Image modifyAlpha removeColor (SIMD on) 41.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.745x (25.5% faster)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 588 seconds

Build and Run Timing

Metric Duration
Simulator Boot 116000 ms
Simulator Boot (Run) 1000 ms
App Install 15000 ms
App Launch 25000 ms
Test Execution 569000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 79ms / native 4ms = 19.7x speedup
SIMD float-mul (64K x300) java 63ms / native 3ms = 21.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 286.000 ms
Base64 CN1 decode 197.000 ms
Base64 native encode 935.000 ms
Base64 encode ratio (CN1/native) 0.306x (69.4% faster)
Base64 native decode 639.000 ms
Base64 decode ratio (CN1/native) 0.308x (69.2% faster)
Base64 SIMD encode 104.000 ms
Base64 encode ratio (SIMD/CN1) 0.364x (63.6% faster)
Base64 SIMD decode 71.000 ms
Base64 decode ratio (SIMD/CN1) 0.360x (64.0% faster)
Base64 encode ratio (SIMD/native) 0.111x (88.9% faster)
Base64 decode ratio (SIMD/native) 0.111x (88.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.375x (62.5% faster)
Image applyMask (SIMD off) 98.000 ms
Image applyMask (SIMD on) 71.000 ms
Image applyMask ratio (SIMD on/off) 0.724x (27.6% faster)
Image modifyAlpha (SIMD off) 91.000 ms
Image modifyAlpha (SIMD on) 78.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.857x (14.3% faster)
Image modifyAlpha removeColor (SIMD off) 141.000 ms
Image modifyAlpha removeColor (SIMD on) 117.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.830x (17.0% faster)

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 375 seconds

Build and Run Timing

Metric Duration
Simulator Boot 63000 ms
Simulator Boot (Run) 0 ms
App Install 14000 ms
App Launch 3000 ms
Test Execution 366000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 4ms = 17.5x speedup
SIMD float-mul (64K x300) java 61ms / native 3ms = 20.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 160.000 ms
Base64 CN1 decode 111.000 ms
Base64 native encode 837.000 ms
Base64 encode ratio (CN1/native) 0.191x (80.9% faster)
Base64 native decode 367.000 ms
Base64 decode ratio (CN1/native) 0.302x (69.8% faster)
Base64 SIMD encode 49.000 ms
Base64 encode ratio (SIMD/CN1) 0.306x (69.4% faster)
Base64 SIMD decode 45.000 ms
Base64 decode ratio (SIMD/CN1) 0.405x (59.5% faster)
Base64 encode ratio (SIMD/native) 0.059x (94.1% faster)
Base64 decode ratio (SIMD/native) 0.123x (87.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 15.000 ms
Image createMask (SIMD on) 62.000 ms
Image createMask ratio (SIMD on/off) 4.133x (313.3% slower)
Image applyMask (SIMD off) 171.000 ms
Image applyMask (SIMD on) 163.000 ms
Image applyMask ratio (SIMD on/off) 0.953x (4.7% faster)
Image modifyAlpha (SIMD off) 83.000 ms
Image modifyAlpha (SIMD on) 124.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.494x (49.4% slower)
Image modifyAlpha removeColor (SIMD off) 92.000 ms
Image modifyAlpha removeColor (SIMD on) 98.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.065x (6.5% slower)

… the log reader

Two defects found in review.

cgroupLimitMB only read the mount-root files. That is correct when the
process has its own cgroup namespace -- the usual container case -- but
under a systemd unit with MemoryMax=, or a container sharing the host
cgroup namespace, the root shows the host's unlimited value and the real
limit lives at the path named in /proc/self/cgroup. In exactly those
configurations the probe fell back to host physical RAM and could pick a
4g heap inside a much smaller container, producing the kernel OOM-kill this
helper exists to prevent. It now resolves the process's own cgroup path and
takes the smallest limit from the mount root down to that leaf, since a
limit set anywhere on the path applies. The mount-root read remains as the
fallback for the namespaced case.

Executor.executeProcess started the output reader thread but never joined
it, returning as soon as waitFor did. Anything inspecting the captured
output afterwards raced a reader that might still be draining the pipe --
and the tail is precisely where a JVM prints its OutOfMemoryError, so the
new translator diagnostic could miss the very text it looks for, while also
reading a StringBuilder under concurrent mutation. The reader is now joined
(bounded at 30s; the process has already exited, so EOF arrives promptly).

The memory-detection test asserted unconditionally that this machine
exposes a budget, which is environment-dependent: detection legitimately
returns -1 on a JVM without the com.sun accessor. It now asserts that only
when the accessor is actually present, which is the condition that makes
detection obligatory, so it still catches a silent regression without being
able to fail for the wrong reason.

Adds cgroup tests covering the systemd/host-namespace layout, a tighter
ancestor limit, v1, the unlimited sentinel, and cgroup paths containing ':'
(legitimate in systemd scope names).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 06:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (1)

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java:166

  • The OOM advice currently says the app needs more heap than the build machine's memory budget allows, but the auto-sized heap can also be constrained by the 4GB ceiling (or other policy choices) even when the machine has plenty of RAM. This wording can mislead users into thinking their machine can't support the build when it may just need a higher configured limit.
    static String outOfMemoryAdvice(int heapMB, boolean canConfigureEnvironment) {
        String head = "The ByteCodeTranslator ran out of memory (it was given -Xmx" + heapMB + "m). "
                + "This app needs a larger translation heap than the build machine's memory budget allows. ";
        if (canConfigureEnvironment) {

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baacb5980b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…s is not failed

Regression from the reader-thread join added in the previous commit. The
watcher loop runs while running[0] is true, and that flag was only cleared
after the join. A command that exited successfully inside its deadline
could therefore have the watcher cross that deadline while the join was
still draining the pipe, set destroyed[0], and make executeProcess return 1
for a run that had already succeeded. The steps that carry a timeout are
the iOS and native translator runs and the CMake builds, so this would have
surfaced as an intermittent "translator failed" on builds that were fine.

running[0] is now cleared immediately after waitFor(), before the join, and
the timeout verdict is captured at that point rather than read afterwards.

The regression test needs the join to actually block, which means the pipe
has to outlive the process: the shell exits at once while a backgrounded
grandchild keeps the inherited stdout open. The first version of this test
used a plain sleeping foreground command, so the join returned instantly,
the watcher never got the chance to fire, and the test passed against the
broken ordering as happily as against the fixed one. Verified the rewritten
one fails on the old ordering (expected 0 but was 1) and passes on the new.

Also covers the two properties either way: that the captured output is
complete when exec returns (the tail is where a JVM prints its
OutOfMemoryError, which is what the translator diagnostic looks for), and
that a genuine overrun is still reported as a timeout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 06:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (5)

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:92

  • This test depends on /bin/sh and also on seq being available in the shell environment. That can make the test fail in otherwise-valid environments (e.g. non-POSIX hosts, or minimal shells without coreutils). Guard on /bin/sh and use a POSIX-shell loop to avoid relying on seq.
        TestExecutor e = new TestExecutor();
        StringBuilder sb = new StringBuilder();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
                "for i in $(seq 1 500); do echo line-$i; done; echo java.lang.OutOfMemoryError: Java heap space");
        int rc = executeCapturing(e, pb, sb);

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:112

  • This test hard-requires /bin/sh, which will fail on Windows/non-POSIX environments. Add an assumption so the test is skipped when /bin/sh isn’t present.
        TestExecutor e = new TestExecutor();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "sleep 30");
        int rc = e.executeProcess(pb, 1000);

vm/ByteCodeTranslator/src/com/codename1/tools/translator/JavascriptBundleWriter.java:239

  • The loop nulls out entries in chunks, but current (line 199) and tail (line 210) still hold references to the last (potentially largest) StringBuilder until the method returns. That keeps an extra full UTF-16 buffer alive during minifyGeneratedIdentifiers()/aliasHotCn1Identifiers() and undermines the peak-memory reduction you’re aiming for.
            chunkStrings.add(chunks.get(i).toString());
            chunks.set(i, null);
        }

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java:165

  • outOfMemoryAdvice() says the app needs more heap than the machine’s memory budget allows, but the heap can be capped by policy (e.g. CEILING_MB) even when the machine/budget would allow more. This wording can mislead users on large machines/boxes where the budget isn’t the limiting factor.
        String head = "The ByteCodeTranslator ran out of memory (it was given -Xmx" + heapMB + "m). "
                + "This app needs a larger translation heap than the build machine's memory budget allows. ";

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:78

  • This test hard-requires /bin/sh, so it will fail on Windows (and any environment without that path) even though it’s exercising Java code. Consider guarding with a JUnit assumption so the suite can still run on non-POSIX dev machines/CI agents.

This issue also appears in the following locations of the same file:

  • line 88
  • line 110
        TestExecutor e = new TestExecutor();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "sleep 3 & echo started; exit 0");
        int rc = e.executeProcess(pb, 1000);

The Linux and Windows native builders never read CN1_TRANSLATOR_OPTS, so an
operator setting -Xmx6g or a diagnostic flag saw it take effect on the
JavaScript and iOS targets and silently do nothing on those two -- the
escape hatch worked or not depending on which target you were building.

The parsing now lives in TranslatorHeap.extraJvmOptions/specifiesHeap and
all four builders call it, replacing three separately-maintained copies of
the same loop with one. That is what let the two native targets drift in
the first place.

While these call sites were open, the two native builders also gained the
out-of-memory diagnostic the JavaScript and iOS builders already had, so a
translator that dies for lack of memory explains itself on every target
rather than four fifths of them.

specifiesHeap deliberately matches only -Xmx: -Xms is not a maximum and
must not suppress the auto-sized ceiling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 06:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (4)

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:92

  • This test hard-codes /bin/sh, which will fail on Windows runners (and any environment without that shell). Please guard it with a JUnit assumption so the suite stays portable.
        StringBuilder sb = new StringBuilder();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
                "for i in $(seq 1 500); do echo line-$i; done; echo java.lang.OutOfMemoryError: Java heap space");
        int rc = executeCapturing(e, pb, sb);

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:113

  • This test hard-codes /bin/sh, which will fail on Windows runners (and any environment without that shell). Please guard it with a JUnit assumption so the suite stays portable.
    void aProcessThatOverrunsItsDeadlineIsStillReportedAsTimedOut() throws Exception {
        TestExecutor e = new TestExecutor();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "sleep 30");
        int rc = e.executeProcess(pb, 1000);
        assertEquals(1, rc, "a genuine timeout must still fail");

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java:2009

  • reader.join(30000) is unconditional, so a genuine timeout can now block for up to an extra 30s after the deadline, and if the reader hasn’t finished by then the method can still return with an incomplete/unstable outputMessage. Consider joining for a shorter period when timedOut is already known, and if the reader is still alive force-close the stream and re-join briefly so the output is finalized before returning.
            // OutOfMemoryError. Bounded so a wedged reader cannot hang the build;
            // the process has already exited, so the stream reaches EOF promptly.
            reader.join(30000);

maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/ExecutorProcessTimeoutTest.java:77

  • This test hard-codes /bin/sh, which will fail on Windows runners (and any environment without that shell). Please guard it with a JUnit assumption so the suite stays portable.

This issue also appears in the following locations of the same file:

  • line 89
  • line 109
        TestExecutor e = new TestExecutor();
        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "sleep 3 & echo started; exit 0");
        int rc = e.executeProcess(pb, 1000);

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d328009e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The three tests added with the timeout fix hard-coded /bin/sh, so on Windows
ProcessBuilder would throw before any of the behaviour under test ran and
the module's suite stopped being runnable off Unix. The plugin is built and
developed on Windows too, so that is a real hole rather than a theoretical
one.

Each child is now a JVM running a helper class from this test, launched
from java.home with the current java.class.path, which behaves the same on
every platform. Skipping the tests off Unix would have been the smaller
change but would have left Windows with no coverage of a fix whose whole
point is that timed steps stop failing spuriously.

The helper that reproduces the regression starts a grandchild inheriting
its stdout and exits immediately, so the pipe outlives the process and the
reader join genuinely blocks -- the property the test depends on. Verified
again after the rewrite that it still fails against the old ordering
(expected 0 but was 1) and passes against the fixed one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 06:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (2)

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java:2010

  • executeProcess() joins the reader thread for up to 30s, but if the pipe outlives the process for longer (e.g. a child process inherits stdout/stderr and keeps it open), join() can time out and the reader may continue appending to outputMessage after executeProcess() returns. That reintroduces the race this change is trying to eliminate and can leave non-daemon threads running unexpectedly. Consider closing the stream and waiting briefly if the reader is still alive after the bounded join.
            reader.join(30000);

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/TranslatorHeap.java:124

  • maxHeapMB() can return a heap larger than the detected memory budget when the historical floor is higher than the budget (e.g. floorMB=2048 on a 1GB cgroup/host). That undermines the stated goal of sizing within the container/machine limit and can cause the forked JVM to fail to start (or be OOM-killed) due to an impossible -Xmx. Consider clamping the auto-sized result to budgetMB as well, even if that means going below the historical floor in undersized environments.
        if (heap < floorMB) {
            heap = floorMB;
        }
        return (int) heap;

@shai-almog
shai-almog merged commit 49a5229 into master Aug 5, 2026
50 checks passed
@shai-almog
shai-almog deleted the fix/translator-heap-large-apps branch August 5, 2026 07:25
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

Successfully merging this pull request may close these issues.

[Bug] building javascript needs more memory

2 participants