{ "type": "excalidraw", "version": 2, "source": "https://excalidraw.com", "elements": [ {"type":"text","id":"title","x":60,"y":10,"width":1120,"height":38, "text":"JVM Anti-Patterns vs Fixes — Quick Reference","fontSize":26,"fontFamily":2, "textAlign":"center","strokeColor":"#1971c2","backgroundColor":"transparent", "roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"sub","x":60,"y":50,"width":1120,"height":20, "text":"Every anti-pattern on the left has a direct drop-in fix on the right. Prioritise top-to-bottom — memory issues cause the most production incidents.", "fontSize":12,"fontFamily":1,"textAlign":"center", "strokeColor":"#666","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"hdr-cat","x":60,"y":76,"width":130,"height":30, "strokeColor":"#333","backgroundColor":"#2c2c2c","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"hdr-cat-t","x":62,"y":82,"width":126,"height":18, "text":"Category","fontSize":12,"fontFamily":2,"textAlign":"center", "strokeColor":"#fff","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"hdr-bad","x":194,"y":76,"width":466,"height":30, "strokeColor":"#c92a2a","backgroundColor":"#c92a2a","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"hdr-bad-t","x":196,"y":82,"width":462,"height":18, "text":"Anti-Pattern","fontSize":13,"fontFamily":2,"textAlign":"center", "strokeColor":"#fff","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"hdr-fix","x":664,"y":76,"width":518,"height":30, "strokeColor":"#0ca678","backgroundColor":"#0ca678","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"hdr-fix-t","x":666,"y":82,"width":514,"height":18, "text":"Fix","fontSize":13,"fontFamily":2,"textAlign":"center", "strokeColor":"#fff","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"cat-mem","x":60,"y":106,"width":130,"height":172, "strokeColor":"#c92a2a","backgroundColor":"#fff5f5","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"cat-mem-t","x":62,"y":176,"width":126,"height":40, "text":"Memory","fontSize":12,"fontFamily":2,"textAlign":"center", "strokeColor":"#c92a2a","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r1","x":194,"y":106,"width":466,"height":40, "strokeColor":"#ffa8a8","backgroundColor":"#fff5f5","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t1bad","x":200,"y":111,"width":454,"height":30, "text":"Hardcoded -Xmx/-Xms in Dockerfile\nBreaks silently when VPA or cluster admin resizes limits", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#c92a2a","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r1f","x":664,"y":106,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t1fix","x":670,"y":111,"width":506,"height":30, "text":"-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0\nReads actual cgroup limit at startup, correct on any instance size", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r2","x":194,"y":148,"width":466,"height":40, "strokeColor":"#ffa8a8","backgroundColor":"#fff5f5","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t2bad","x":200,"y":153,"width":454,"height":30, "text":"MaxRAMPercentage=90 leaves almost nothing for off-heap\nStarves Metaspace, thread stacks, JIT cache, Netty direct buffers", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#c92a2a","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r2f","x":664,"y":148,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t2fix","x":670,"y":153,"width":506,"height":30, "text":"MaxRAMPercentage=75 reserves 25% for off-heap regions\nMeasure first with: jcmd PID VM.native_memory summary", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r3","x":194,"y":190,"width":466,"height":40, "strokeColor":"#ffa8a8","backgroundColor":"#fff5f5","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t3bad","x":200,"y":195,"width":454,"height":30, "text":"No -XX:MaxMetaspaceSize set\nFramework apps can silently grow Metaspace to 800 MB+", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#c92a2a","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r3f","x":664,"y":190,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t3fix","x":670,"y":195,"width":506,"height":30, "text":"-XX:MaxMetaspaceSize=256m caps unbounded growth\nIncrease to 512m if ClassNotFoundException at startup", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r4","x":194,"y":232,"width":466,"height":46, "strokeColor":"#ffa8a8","backgroundColor":"#fff5f5","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t4bad","x":200,"y":237,"width":454,"height":36, "text":"memory requests == memory limits (no headroom)\nOOMKill at P99 load peak when GC surges beyond limit", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#c92a2a","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r4f","x":664,"y":232,"width":518,"height":46, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t4fix","x":670,"y":237,"width":506,"height":36, "text":"requests = P50 RSS, limits = P99 RSS + 25% headroom\nMeasure with: kubectl top pod --containers", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"cat-gc","x":60,"y":282,"width":130,"height":168, "strokeColor":"#e67700","backgroundColor":"#fff9f0","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"cat-gc-t","x":62,"y":354,"width":126,"height":40, "text":"GC and CPU","fontSize":12,"fontFamily":2,"textAlign":"center", "strokeColor":"#e67700","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r5","x":194,"y":282,"width":466,"height":40, "strokeColor":"#ffd8a8","backgroundColor":"#fff9f0","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t5bad","x":200,"y":287,"width":454,"height":30, "text":"Default ParallelGCThreads on large node\n64-core node + 2-CPU container = 64 GC threads, 2 CPUs available", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#e67700","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r5f","x":664,"y":282,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t5fix","x":670,"y":287,"width":506,"height":30, "text":"-XX:ParallelGCThreads=N where N equals your CPU request\nMatch GC thread count to what the scheduler will actually give you", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r6","x":194,"y":324,"width":466,"height":40, "strokeColor":"#ffd8a8","backgroundColor":"#fff9f0","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t6bad","x":200,"y":329,"width":454,"height":30, "text":"CPU-based HPA with Java workloads\nGC stop-the-world spikes CPU, HPA scales out for the wrong reason", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#e67700","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r6f","x":664,"y":324,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t6fix","x":670,"y":329,"width":506,"height":30, "text":"Scale on RPS (requests/second) via KEDA or Prometheus Adapter\nRPS is GC-immune and reflects actual application load", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r7","x":194,"y":366,"width":466,"height":40, "strokeColor":"#ffd8a8","backgroundColor":"#fff9f0","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t7bad","x":200,"y":371,"width":454,"height":30, "text":"minReplicas: 1 in HPA\nSingle pod GC pause means 100% request error rate", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#e67700","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r7f","x":664,"y":366,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t7fix","x":670,"y":371,"width":506,"height":30, "text":"minReplicas: 2 so load balancer routes around the pausing pod\nCheapest reliability improvement available to you", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r8","x":194,"y":408,"width":466,"height":42, "strokeColor":"#ffd8a8","backgroundColor":"#fff9f0","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t8bad","x":200,"y":413,"width":454,"height":32, "text":"No HPA stabilizationWindowSeconds\nGC CPU spikes cause scale-out/in thrash every few minutes", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#e67700","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r8f","x":664,"y":408,"width":518,"height":42, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t8fix","x":670,"y":413,"width":506,"height":32, "text":"behavior.scaleUp.stabilizationWindowSeconds: 120\nLonger than any normal GC pause, absorbs spikes before acting", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"cat-aot","x":60,"y":454,"width":130,"height":168, "strokeColor":"#1971c2","backgroundColor":"#e8f4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"cat-aot-t","x":62,"y":524,"width":126,"height":40, "text":"AOT Startup","fontSize":12,"fontFamily":2,"textAlign":"center", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r9","x":194,"y":454,"width":466,"height":40, "strokeColor":"#a5d8ff","backgroundColor":"#e8f4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t9bad","x":200,"y":459,"width":454,"height":30, "text":"Using @QuarkusTest for AOT training\nRuns in dev JVM, contributes nothing to app.aot cache", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r9f","x":664,"y":454,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t9fix","x":670,"y":459,"width":506,"height":30, "text":"@QuarkusIntegrationTest runs against packaged JAR and trains cache\nMore endpoints covered means richer cache means faster every start", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r10","x":194,"y":496,"width":466,"height":40, "strokeColor":"#a5d8ff","backgroundColor":"#e8f4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t10bad","x":200,"y":501,"width":454,"height":30, "text":"Manual -XX:SharedArchiveFile in Dockerfile\nQuarkus sets this automatically, duplicate flag causes startup failure", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r10f","x":664,"y":496,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t10fix","x":670,"y":501,"width":506,"height":30, "text":"Remove from Dockerfile. Set quarkus.package.jar.aot.enabled=true\nQuarkus plugin writes -XX:AOTCache=app.aot into the package", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r11","x":194,"y":538,"width":466,"height":40, "strokeColor":"#a5d8ff","backgroundColor":"#e8f4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t11bad","x":200,"y":543,"width":454,"height":30, "text":"mvn package instead of mvn verify for AOT\nPackage skips integration tests so cache is empty or minimal", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r11f","x":664,"y":538,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t11fix","x":670,"y":543,"width":506,"height":30, "text":"./mvnw verify runs build + integration tests + writes app.aot\nDrop-in replacement in any CI/CD pipeline", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r12","x":194,"y":580,"width":466,"height":42, "strokeColor":"#a5d8ff","backgroundColor":"#e8f4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t12bad","x":200,"y":585,"width":454,"height":32, "text":"Unpinned JDK FROM eclipse-temurin:25-jre in Dockerfile\nMinor JDK update invalidates app.aot, silent rebuild, zero gain", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r12f","x":664,"y":580,"width":518,"height":42, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t12fix","x":670,"y":585,"width":506,"height":30, "text":"FROM eclipse-temurin:25.0.1-jre pin exact version\nCache is tied to JDK version, mismatch triggers silent full rebuild", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"cat-obs","x":60,"y":626,"width":130,"height":168, "strokeColor":"#2f9e44","backgroundColor":"#f0fdf4","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"cat-obs-t","x":62,"y":698,"width":126,"height":40, "text":"Observability","fontSize":12,"fontFamily":2,"textAlign":"center", "strokeColor":"#2f9e44","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r13","x":194,"y":626,"width":466,"height":40, "strokeColor":"#b2f2bb","backgroundColor":"#f0fdf4","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t13bad","x":200,"y":631,"width":454,"height":30, "text":"No GC pause histogram in Prometheus\nCannot separate GC-induced latency from real application slowness", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#2f9e44","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r13f","x":664,"y":626,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t13fix","x":670,"y":631,"width":506,"height":30, "text":"quarkus.micrometer.distribution.percentiles-histogram.jvm.gc.pause=true\nEnables P99 GC pause bucket. One line in application.properties.", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r14","x":194,"y":668,"width":466,"height":40, "strokeColor":"#b2f2bb","backgroundColor":"#f0fdf4","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t14bad","x":200,"y":673,"width":454,"height":30, "text":"quarkus-micrometer-registry-prometheus only\nMetrics disconnected from traces, missing unified OTel pipeline", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#2f9e44","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r14f","x":664,"y":668,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t14fix","x":670,"y":673,"width":506,"height":30, "text":"quarkus-micrometer-opentelemetry unifies metrics + traces + logs\nSingle extension replaces registry-prometheus and opentelemetry", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r15","x":194,"y":710,"width":466,"height":40, "strokeColor":"#b2f2bb","backgroundColor":"#f0fdf4","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t15bad","x":200,"y":715,"width":454,"height":30, "text":"No PrometheusRule alert on jvm_gc_pause\nGC degradation invisible until SLO breach, no warning before impact", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#2f9e44","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r15f","x":664,"y":710,"width":518,"height":40, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t15fix","x":670,"y":715,"width":506,"height":30, "text":"Alert: histogram_quantile(0.99, jvm_gc_pause_seconds) > 0.5 for 2m\nFires before users notice, gives 5-10 min lead time on incidents", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r16","x":194,"y":752,"width":466,"height":42, "strokeColor":"#b2f2bb","backgroundColor":"#f0fdf4","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t16bad","x":200,"y":757,"width":454,"height":32, "text":"Tuning JVM flags without a baseline measurement\nNo before/after data, cannot tell if a change helped or hurt", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#2f9e44","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"r16f","x":664,"y":752,"width":518,"height":42, "strokeColor":"#63e6be","backgroundColor":"#f0fdf8","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"t16fix","x":670,"y":757,"width":506,"height":32, "text":"Measure P99 startup + P99 latency + P99 GC pause before any change\nChange one flag at a time. Record. Compare. Conclude.", "fontSize":11,"fontFamily":1,"textAlign":"left", "strokeColor":"#0ca678","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0}, {"type":"rectangle","id":"footer-bar","x":60,"y":800,"width":1122,"height":34, "strokeColor":"#1971c2","backgroundColor":"#dbe4ff","fillStyle":"solid","roughness":0,"opacity":100,"angle":0}, {"type":"text","id":"footer-txt","x":65,"y":809,"width":1112,"height":18, "text":"Priority order: Memory flags first, then minReplicas:2, then ParallelGCThreads, then HPA metric, then @QuarkusIntegrationTest, then GC histogram, then baseline", "fontSize":11,"fontFamily":2,"textAlign":"center", "strokeColor":"#1971c2","backgroundColor":"transparent","roughness":0,"opacity":100,"angle":0} ], "appState": { "gridSize": null, "viewBackgroundColor": "#ffffff" } }