Zero-Allocation Programming
Zero-Allocation programming is a prime example of Innovation meeting elite performance. In this guide, we’ll discover how to overcome the inherent limitations of the Java Virtual Machine to ensure our robot code is deterministic, safe, and ready for the highest level of Impact. By mastering these patterns, you ensure that our collective Teamwork is never interrupted by a garbage collector pause.
What is Zero-Allocation?
Section titled “What is Zero-Allocation?”In simple terms: Zero-allocation means reusing objects instead of creating new ones.
Think of it like this:
- ❌ With allocation: Every time you need a cup, you buy a new one.
- ✅ Zero-allocation: You wash and reuse the same cup.
Why it matters: Creating objects takes time and memory. Your robot needs to respond quickly - every 20 milliseconds! Creating too many objects makes your robot slow and stuttery.
The Problem: Garbage Collection
Section titled “The Problem: Garbage Collection”Java’s Garbage Collector (GC) is like a trash service that cleans up objects you no longer need.
The problem:
- When GC runs, it pauses everything else.
- Your robot stops responding during GC.
- This causes missed shots, crashes, and inconsistent performance.
Real impact:
- Your auto routine might miss the target.
- Robot stutters during matches.
- Unpredictable behavior at critical moments.
The 20ms Deadline
Section titled “The 20ms Deadline”FRC robots run on a tight schedule:
- Main loop: Every 20ms (50 times per second)
- Odometry loop: Every 4ms (250 times per second)
If GC runs:
- Your 20ms loop might take 40ms or longer.
- Robot’s position becomes inaccurate.
- Robot might crash or miss critical shots.
The Solution: Reuse Objects
Section titled “The Solution: Reuse Objects”Instead of creating new objects, reuse the same ones.
❌ Wrong: Creates New Object Every Loop
Section titled “❌ Wrong: Creates New Object Every Loop”@Overridepublic void periodic() { // BAD: Creates new Translation2d every 20ms! Translation2d position = new Translation2d(x, y);
// More bad object creation Pose2d robotPose = new Pose2d(position, new Rotation2d(heading));}Why this is bad: Each new creates trash. The GC has to clean it up later.
✅ Right: Reuses Objects
Section titled “✅ Right: Reuses Objects”// Create once, reuse foreverprivate final Translation2d position = new Translation2d(0, 0);private final Pose2d robotPose = new Pose2d();
@Overridepublic void periodic() { // Reuse the same objects position.setX(x); position.setY(y);
robotPose.setPose(position, new Rotation2d(heading));}Why this is good: No trash created, no GC needed, runs fast and smooth!
What NOT to Do in Loops
Section titled “What NOT to Do in Loops”Never do these in periodic() or any loop:
❌ No new Keywords
Section titled “❌ No new Keywords”// BAD: Creates new object every loopPose2d currentPose = new Pose2d(x, y, new Rotation2d(theta));
// GOOD: Reuse objectprivate final Pose2d currentPose = new Pose2d();// In periodic(): currentPose.setX(x); currentPose.setY(y);❌ No Lambdas
Section titled “❌ No Lambdas”// BAD: Creates new lambda every loopcommands.add(() -> drivetrain.drive(0.5));
// GOOD: Create command onceprivate final DriveCommand driveCommand = new DriveCommand(drivetrain, 0.5);commands.add(driveCommand);❌ No String Building
Section titled “❌ No String Building”// BAD: Creates new strings every loopString status = "Mode: " + mode + " Speed: " + speed;
// GOOD: Use Logger or pre-buildLogger.recordOutput("Status", mode);// Or use StringBuilderprivate final StringBuilder statusBuilder = new StringBuilder(50);statusBuilder.setLength(0); // ClearstatusBuilder.append("Mode: ").append(mode);Try It Yourself
Section titled “Try It Yourself”Use the simulator below to see how object creation affects loop time:
What to notice:
- Click “ALLOCATE OBJECT” and watch loop time spike.
- Watch GC pause everything when trash piles up.
- Compare to “REUSE OBJECT” - smooth and fast!
MARSLib Makes This Easy
Section titled “MARSLib Makes This Easy”MARSLib handles zero-allocation for you using IO Inputs.
How It Works
Section titled “How It Works”IO Inputs are pre-built objects that hold all your data:
// MARSLib creates these for youpublic static class DrivetrainInputs { public double positionX; public double positionY; public double velocityX; public double velocityY; // ... more data}
// You just update values@Overridepublic void periodic() { inputs.positionX = odometry.getX(); inputs.positionY = odometry.getY(); // No new objects created!}Why this works:
- Inputs objects created once at startup.
- You just update the values.
- Zero trash, zero GC, maximum speed!
Why MARSLib Uses This Pattern
Section titled “Why MARSLib Uses This Pattern”Benefits you get:
- Faster loop times: Your code runs quicker.
- Smoother operation: No GC pauses.
- Reliable auto: Auto routines work every time.
- Better performance: Can run more complex code.
Real-world results:
- Loop times: 5ms instead of 15ms.
- No stuttering or lag.
- Auto accuracy improves dramatically.
- Robot responds predictably.
Common Patterns to Avoid
Section titled “Common Patterns to Avoid”String Concatenation
Section titled “String Concatenation”// BADString message = "Robot at: " + pose.getX() + ", " + pose.getY();
// GOOD - Use LoggerLogger.recordOutput("Robot/X", pose.getX());Logger.recordOutput("Robot/Y", pose.getY());Math Object Creation
Section titled “Math Object Creation”// BADTranslation2d movement = new Translation2d(dx, dy);
// GOOD - Update existingprivate final Translation2d movement = new Translation2d(0, 0);// In periodic():movement.setX(dx);movement.setY(dy);Collection Creation
Section titled “Collection Creation”// BADList<Double> values = new ArrayList<>();
// GOOD - Reuse listprivate final List<Double> values = new ArrayList<>();// In periodic():values.clear();values.add(speed1);values.add(speed2);Testing Your Code
Section titled “Testing Your Code”Check for allocations:
1 Look for new Keywords
Section titled “1 Look for new Keywords”# Search for new keywords in periodic methodsgrep -n "new " src/main/java/frc/robot/subsystems/2 Monitor Loop Time
Section titled “2 Monitor Loop Time”@Overridepublic void periodic() { double startTime = Timer.getFPGATimestamp();
// Your code here
double loopTime = (Timer.getFPGATimestamp() - startTime) * 1000; SmartDashboard.putNumber("LoopTime_ms", loopTime);
// Warning if too slow if (loopTime > 10.0) { DriverStation.reportWarning("Loop too slow: " + loopTime + "ms", false); }}Good: Loop time < 10ms Warning: Loop time 10-15ms Problem: Loop time > 15ms
3 Use Java Flight Recorder
Section titled “3 Use Java Flight Recorder”# Run with profilingjava -XX:+PrintGCDetails -XX:+PrintGCTimeStamps YourRobotLook for GC pauses in the output.
Quick Reference Card
Section titled “Quick Reference Card”In loops (periodic, execute, etc.):
- ✅ Reuse objects.
- ✅ Update values instead of creating new.
- ✅ Use Logger for output.
- ❌ No
newkeywords. - ❌ No lambdas
- ❌ No string building.
Outside loops:
- ✅ Create objects once in constructor.
- ✅ Use static final constants.
- ✅ Pre-allocate collections.
Real-World Example
Section titled “Real-World Example”Before: Slow Code
Section titled “Before: Slow Code”public class Drivetrain { public void periodic() { // Creates 3 new objects every 20ms! Pose2d pose = new Pose2d(odometry.getX(), odometry.getY(), new Rotation2d(odometry.getHeading())); ChassisSpeeds speeds = new ChassisSpeeds(forward, strafe, rotation); SwerveModuleState[] states = kinematics.toSwerveModuleStates(speeds);
// ... more code }}Problems:
- Creates 3 objects every 20ms.
- 150 objects per second.
- Causes GC pauses.
- Loop time: 12-15ms.
After: Fast Code
Section titled “After: Fast Code”public class Drivetrain { // Create once, reuse forever private final Pose2d pose = new Pose2d(); private final ChassisSpeeds speeds = new ChassisSpeeds(0, 0, 0); private final SwerveModuleState[] states = new SwerveModuleState[4];
public void periodic() { // Just update values pose.setX(odometry.getX()); pose.setY(odometry.getY()); pose.setRotation(new Rotation2d(odometry.getHeading()));
speeds.vxMetersPerSecond = forward; speeds.vyMetersPerSecond = strafe; speeds.omegaRadiansPerSecond = rotation;
// ... more code }}Benefits:
- Zero objects created.
- No GC pauses
- Loop time: 3-5ms.
- Robot runs smoothly!
Competition Tips
Section titled “Competition Tips”Before competition:
- Profile your code (check loop times)
- Look for allocations in loops.
- Fix any issues you find.
- Test under load (run for 5+ minutes)
During competition:
- Monitor loop times on dashboard.
- Watch for performance warnings.
- Keep code clean and fast.
Debugging slow code:
- Add timing prints to find slow code.
- Check for
newkeywords. - Look for string building.
- Profile each subsystem.
Advanced Topics
Section titled “Advanced Topics”Object Pooling
Section titled “Object Pooling”For complex objects, use a pool:
public class ObjectPool<T> { private final Queue<T> pool = new LinkedList<>();
public T get() { T obj = pool.poll(); if (obj == null) { obj = createNew(); } return obj; }
public void return(T obj) { pool.offer(obj); }}Memory Pre-Allocation
Section titled “Memory Pre-Allocation”Pre-allocate large structures:
// Allocate onceprivate final double[] sensorData = new double[100];
// Use many times@Overridepublic void periodic() { for (int i = 0; i < sensorCount; i++) { sensorData[i] = sensors[i].get(); } // No new arrays created!}Summary
Section titled “Summary”Key points to remember:
- Zero-allocation = Reuse objects, don’t create new ones.
- GC pauses make your robot slow and unpredictable.
- MARSLib IO inputs handle this for you automatically.
- Avoid
new, lambdas, and string building in loops. - Monitor loop times to catch performance issues.
Your robot will:
- Run faster and smoother.
- Respond more predictably.
- Perform better in autonomous.
- Be more reliable in competition.
Next Steps
Section titled “Next Steps”- Performance Guide - More optimization tips.
- Code Best Practices - How to write fast code.
- Testing Guide - Test your code performance.
Remember: Fast code wins competitions! Zero-allocation programming is one of the most important skills for competitive FRC programming.
Need help optimizing your code? Ask our community