• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Guru: From Hiking Boots To Long-Term Memory

    November 9, 2026 Gregory Simmons

    In my last article, “Environment Variables: Global Variables Wearing Hiking Boots,” we explored how environment variables can carry information around an IBM i job, making it available to programs and procedures along the way.

    But what if our variables don’t need to go hiking? What if we simply need them to remember where they’ve been? As RPG developers, we’re accustomed to declaring variables, assigning values, and expecting those values to be there when we need them. But have you ever considered what happens to those variables when a procedure ends? Or what happens when you call that same procedure again?

    Some variables start fresh every time. Others have surprisingly good memories. Let’s head back into the mushroom patch and see what happens.

    Some Mushrooms Have Better Memories Than Others

    Consider two simple procedures. Both count mushrooms, but there’s one small difference in how their variables are declared.

    Our first procedure uses an ordinary local variable:

    dcl-proc countAutomatic;
      dcl-pi *n int(10);
      end-pi;
    
      dcl-s mushrooms int(10) inz(0);
      mushrooms += 1;
    
      return mushrooms;
    end-proc;
    

    Every time we call countAutomatic(), the variable mushrooms is initialized to zero. We increment it and return 1.

    Call the procedure three times, and we get:

    1
    1
    1
    

    Now let’s make one small change:

    dcl-proc countStatic;
      dcl-pi *n int(10);
      end-pi;
    
      dcl-s mushrooms int(10) inz(0) static;
      mushrooms += 1;
    
      return mushrooms;
    end-proc;
    

    We’ve added the STATIC keyword. Call this procedure three times, and the results are quite different:

    1
    2
    3
    

    Instead of initializing mushrooms every time the procedure is called, RPG initializes the static variable when its containing program or service program is activated. Its value is retained between calls. Notice that the variable is still local to the procedure. No other procedure can directly access mushrooms, even though its value survives after the procedure returns.

    And here’s an important distinction: static doesn’t mean global. Global describes where a variable can be accessed, while static describes how its storage is maintained. Global RPG variables generally have static storage, but our mushrooms variable is both local and static. So far, nothing particularly surprising. But let’s see what happens when we introduce a service program.

    When Mushrooms Become A Service

    Suppose we create a module named MUSHSRV containing an exported procedure called COUNTMUSHROOMS():

    **free
    ctl-opt nomain;
    dcl-proc COUNTMUSHROOMS export;
      dcl-pi *n int(10);
      end-pi;
    
      dcl-s mushrooms int(10) inz(0) static;
      mushrooms += 1;
    
      return mushrooms;
    end-proc;
    

    Nothing complicated here. Every time the procedure is called, it increments its static counter and returns the result.

    Now we will create two programs, MUSHMAIN and MUSHOTHER, that call this same procedure. Each program also has its own local static counter so we can compare the behavior.

    Here’s MUSHMAIN:

    **free
    dcl-pr COUNTMUSHROOMS int(10);
    end-pr;
    
    dcl-s localCount int(10);
    dcl-s serviceCount int(10);
    
    localCount = countLocal();
    serviceCount = COUNTMUSHROOMS();
    
    dsply ('MAIN local: ' + %char(localCount));
    dsply ('MAIN service: ' + %char(serviceCount));
    
    return;
    
    dcl-proc countLocal;
      dcl-pi *n int(10);
      end-pi;
    
      dcl-s mushrooms int(10) inz(0) static;
      mushrooms += 1;
    
      return mushrooms;
    end-proc;
    

    Our second program, MUSHOTHER, uses identical logic, except that OTHER replaces MAIN in the display statements.

    **free
    dcl-pr COUNTMUSHROOMS int(10);
    end-pr;
    
    dcl-s localCount int(10);
    dcl-s serviceCount int(10);
    
    localCount = countLocal();
    serviceCount = COUNTMUSHROOMS();
    
    dsply ('OTHER local: ' + %char(localCount));
    dsply ('OTHER service: ' + %char(serviceCount));
    return;
    
    dcl-proc countLocal;
      dcl-pi *n int(10);
      end-pi;
    
      dcl-s mushrooms int(10) inz(0) static;
      mushrooms += 1;
    
      return mushrooms;
    end-proc;
    

    Same Service Program, Different Memories

    Here’s where things get interesting. We’ll create the service program with ACTGRP(*CALLER) and place our two calling programs in different named activation groups.

    For these examples, we’ll use a test library named MUSHLIB, with the source members stored in QRPGLESRC.

    First, compile the module and create the service program:

    CRTRPGMOD MODULE(MUSHLIB/MUSHSRV) SRCFILE(MUSHLIB/QRPGLESRC)
    CRTSRVPGM SRVPGM(MUSHLIB/MUSHSRVC)  MODULE(MUSHLIB/MUSHSRV)  EXPORT(*ALL) 
              ACTGRP(*CALLER)
    

    Next, create the two calling programs:

    CRTRPGMOD MODULE(MUSHLIB/MUSHMAIN) SRCFILE(MUSHLIB/QRPGLESRC)
    
    CRTPGM PGM(MUSHLIB/MUSHMAIN) MODULE(MUSHLIB/MUSHMAIN) 
           BNDSRVPGM(MUSHLIB/MUSHSRVC) ACTGRP(MUSHGRPA)
    
    CRTRPGMOD MODULE(MUSHLIB/MUSHOTHER) SRCFILE(MUSHLIB/QRPGLESRC)
    
    CRTPGM PGM(MUSHLIB/MUSHOTHER) MODULE(MUSHLIB/MUSHOTHER) 
           BNDSRVPGM(MUSHLIB/MUSHSRVC) ACTGRP(MUSHGRPB)
    

    Notice that we have placed MUSHMAIN in activation group MUSHGRPA and MUSHOTHER in MUSHGRPB. Because these are named activation groups, their activations remain available between calls, allowing their static counters to retain their values.

    Now let’s call the programs in this order, using the same IBM i job:

    CALL MUSHLIB/MUSHMAIN
    CALL MUSHLIB/MUSHMAIN
    CALL MUSHLIB/MUSHOTHER
    CALL MUSHLIB/MUSHMAIN
    CALL MUSHLIB/MUSHOTHER
    

    Here’s what we should see:

    Program        	Local Counter  	Service Counter
    MUSHMAIN                 1		   1
    MUSHMAIN                 2                 2
    MUSHOTHER             	 1                 1
    MUSHMAIN                 3                 3
    MUSHOTHER           	 2                 2
    

    Wait a minute. Both programs are calling the same service program, yet the service counter started over when MUSHOTHER made its first call! That’s because we created the service program with ACTGRP(*CALLER).

    When MUSHMAIN calls the service program, it is activated in MUSHGRPA. When MUSHOTHER calls it, another activation is created in MUSHGRPB. Each activation has its own static storage, including its own copy of mushrooms. In other words, our service program has two separate memories.

    Let’s Give Those Mushrooms A Shared Memory

    Instead of creating our service program with ACTGRP(*CALLER), we’ll give it its own named activation group. To make the comparison easier, we’ll create a second service program named MUSHSRVS, using the same RPG module. This lets us compare the two configurations without replacing our original service program.

    CRTSRVPGM SRVPGM(MUSHLIB/MUSHSRVS) MODULE(MUSHLIB/MUSHSRV) 
              EXPORT(*ALL) ACTGRP(MUSHSHARE)
    

    We will create two new program objects from our existing caller modules, binding them to the new service program.

    CRTPGM PGM(MUSHLIB/MUSHMN2) MODULE(MUSHLIB/MUSHMAIN) 
           BNDSRVPGM(MUSHLIB/MUSHSRVS) ACTGRP(MUSHGRPA)
    
    CRTPGM PGM(MUSHLIB/MUSHOT2) MODULE(MUSHLIB/MUSHOTHER) 
           BNDSRVPGM(MUSHLIB/MUSHSRVS) ACTGRP(MUSHGRPB)
    

    Before running this second experiment, reclaim the two caller activation groups so we start with a clean slate.

    RCLACTGRP ACTGRP(MUSHGRPA)
    RCLACTGRP ACTGRP(MUSHGRPB)
    

    Now repeat the experiment using our new program objects:

    CALL MUSHLIB/MUSHMN2
    CALL MUSHLIB/MUSHMN2
    CALL MUSHLIB/MUSHOT2
    CALL MUSHLIB/MUSHMN2
    CALL MUSHLIB/MUSHOT2
    

    You should see the following results:

    Program    Local Counter  Service Counter
    MUSHMN2                1                1
    MUSHMN2                2                2
    MUSHOT2                1                3
    MUSHMN2                3                4
    MUSHOT2                2                5
    

    Now that’s interesting!

    The local counters still maintain their own values, but both programs are incrementing the same service program counter. We haven’t changed a single line of RPG source code. All we’ve changed is the activation group in which our service program runs.

    With ACTGRP(MUSHSHARE), both callers use the same service program activation within the job. That means they also use the same static mushroom counter. The STATIC keyword hasn’t changed its behavior. What changed is which activation owns that static storage.

    But Don’t Forget About the Job

    Before you start thinking you’ve discovered a clever way to share information throughout your IBM i system, there’s one more thing to remember. Activation groups belong to jobs. If we run MUSHMN2 in one job and MUSHOT2 in another, each job has its own MUSHSHARE activation group and its own copy of the static mushroom counter. The fact that both activation groups have the same name doesn’t mean they share storage across jobs.

    That’s an important difference from the system-level environment variables we discussed in the previous article, which can make named values available to multiple jobs. And if we end an activation group and subsequently reactivate the service program, its static variables are initialized again. Long-term memory doesn’t necessarily mean permanent memory.

    When Is STATIC Useful?

    Static local variables can be handy when a procedure needs to remember something between calls without exposing that information as a global variable.

    Perhaps you are counting how many times a procedure has been called, retaining a previously calculated value, or maintaining some internal state that belongs to a service program. But be aware, a procedure that retains information between calls may behave differently depending on what happened earlier in the job. That can make troubleshooting more difficult, especially when multiple programs (in the same job) use the same service program. And if your application uses multiple threads, shared static storage introduces additional considerations for synchronization and thread safety. As with many RPG features, understanding when to use STATIC is just as important as understanding how it works.

    Back To Our Hiking Boots

    In my previous article, environment variables gave us a way to carry information around our IBM i jobs. This time, we’ve discovered that some RPG variables don’t need to travel anywhere. They can remain right where they are, remembering what happened the last time we visited. The trick is understanding where that memory lives, who can access it, and what causes it to disappear. After all, there’s nothing wrong with having a good memory.

    Unless, of course, your RPG program remembers something you expected it to forget.

    Until next time, happy coding.

    Gregory Simmons is a Project Manager with PC Richard & Son. He started on the IBM i platform in 1994, graduated with a degree in Computer Information Systems in 1997 and has been working on the OS/400 and IBM i platform ever since. He has been a registered instructor with the IBM Academic Initiative since 2007, an IBM Champion and holds a COMMON Application Developer certification. When he’s not trying to figure out how to speed up legacy programs, he enjoys speaking at technical conferences, running, backpacking, hunting, and fishing.

    RELATED STORIES

    Guru: Environment Variables – Global Variables Wearing Hiking Boots

    Guru: When RNF0722 Won’t Let Your Legendary RPG Program Compile

    Guru: Two More Small But Handy RPG Enhancements: LIKE(*EXT) And %LOOKUPNE

    Guru: Beyond Three-Part Naming – Running SQL Across Remote IBM i Systems

    Guru: Finding Data In The Forest – Exploring Three-Part Naming In SQL

    Guru: SQL Sequences In RPG Let Db2 Handle The Counting

    Guru: IBM i Job Log Detective Brings Structure To Job Log Analysis In VS Code

    Guru: Managing The Lifecycle Of Your Service Programs – Updates Without Chaos

    Guru: Are Binding Directories A Shortcut Or A Source Of Chaos?

    Guru: Service Programs And Activation Groups – Design Decisions That Matter

    Guru: Binder Source Is Your Service Program’s Owner’s Manual

    Guru: Access Client Solutions 1.1.9.11 – Security First, With Continued Investment In SQL Tooling

    Guru: Taming The CRTSRVPGM Command – Options That Can Save Your Sanity

    Guru: CRTSRVPGM Parameters That Can Save or Sink You

    Guru: A First Look at Bob, The IBM i Assistant That’s Closer Than You Think

    Bob More Than Just A Code Assistant, IBM i Chief Architect Will Says

    IBM Pulls The Curtain Back A Smidge On Project Bob

    Big Blue Converges IBM i RPG And System Z COBOL Code Assistants Into “Project Bob”

    Guru: When Attention Turns To You – Writing Your Own ATTN Program

    Guru: WCA4i And Granite – Because You’ve Got Bigger Things To Build

    Guru: When Procedure Driven RPG Really Works

    Guru: Unlocking The Power Of %CONCAT And %CONCATARR In RPG

    Guru: AI Pair Programming In RPG With Continue

    Guru: AI Pair Programming In RPG With GitHub Copilot

    Guru: RPG Receives Enumerator Operator

    Guru: RPG Select Operation Gets Some Sweet Upgrades

    Guru: Growing A More Productive Team With Procedure Driven RPG

    Guru: With Procedure Driven RPG, Be Precise With Options(*Exact)

    Guru: Testing URLs With HTTP_GET_VERBOSE

    Guru: Fooling Around With SQL And RPG

    Guru: Procedure Driven RPG And Adopting The Pillars Of Object-Oriented Programming

    Guru: Getting Started With The Code 4 i Extension Within VS Code

    Guru: Procedure Driven RPG Means Keeping Your Variables Local

    Guru: Procedure Driven RPG With Linear-Main Programs

    Guru: Speeding Up RPG By Reducing I/O Operations, Part 2

    Guru: Speeding Up RPG By Reducing I/O Operations, Part 1

    Guru: Watch Out For This Pitfall When Working With Integer Columns

    Share this:

    • Share on Reddit (Opens in new window) Reddit
    • Share on Facebook (Opens in new window) Facebook
    • Share on LinkedIn (Opens in new window) LinkedIn
    • Share on X (Opens in new window) X
    • Email a link to a friend (Opens in new window) Email

    Tags: Tags: 400guru, FHG, Four Hundred Guru, IBM i, RPG

    Sponsored by
    ARCAD Software

    [IBM i Auditing Tool] Discover the Health of Your IBM i Applications in Minutes with ARCAD iCheck-up

    How well do you really know the health of your IBM i applications? Hidden complexity, unused code, and synchronization issues can make maintenance more challenging and slow down modernization efforts.

    What if you could identify these issues in just a few clicks, without installing anything on your IBM i system?

    Meet ARCAD iCheck-up, an auditing tool designed to give IBM i teams a clear, actionable overview of their applications in minutes.

    With iCheck-up, you can:

    • Assess code complexity: Identify complex source files and prioritize refactoring efforts.
    • Detect dead code: Uncover unused code that may be cluttering your applications.
    • Spot synchronization issues: Identify inconsistencies between source files and compiled objects.
    • Track code changes: Explore modification history and better understand your application’s evolution.
    • Generate a shareable report: Get a comprehensive, easy-to-read HTML report with key metrics and insights.

    Fast, simple, and secure. iCheck-up runs from Windows, requires no installation on your IBM i system, and leaves no files or code behind. Your generated report contains metrics only, keeping your source code confidential.

    Whether you’re maintaining existing applications, planning modernization, or looking to improve code quality, iCheck-up helps you make more informed decisions.

    Ready to gain deeper insights into your IBM i applications? Discover ARCAD iCheck-up and take the first step toward better code quality today!

    Share this:

    • Share on Reddit (Opens in new window) Reddit
    • Share on Facebook (Opens in new window) Facebook
    • Share on LinkedIn (Opens in new window) LinkedIn
    • Share on X (Opens in new window) X
    • Email a link to a friend (Opens in new window) Email

    Inside IBM’s New PowerHA Cloud Offering, Full System Manager IBM i In The Cloud And The Case For Reversing Replication

    Leave a ReplyCancel reply

TFH Volume: 36 Issue: 39

This Issue Sponsored By

  • FalconStor
  • Maxava
  • ARCAD Software
  • Raz-Lee Security
  • WorksRight Software

Table of Contents

  • Bob Code Assistant Gets Better Understanding Of IBM i Applications
  • IBM i In The Cloud And The Case For Reversing Replication
  • Guru: From Hiking Boots To Long-Term Memory
  • Inside IBM’s New PowerHA Cloud Offering, Full System Manager
  • IBM i PTF Guide, Volume 28, Number 39

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Bob Code Assistant Gets Better Understanding Of IBM i Applications
  • IBM i In The Cloud And The Case For Reversing Replication
  • Guru: From Hiking Boots To Long-Term Memory
  • Inside IBM’s New PowerHA Cloud Offering, Full System Manager
  • IBM i PTF Guide, Volume 28, Number 39
  • Power Systems Turns In Another Great Quarter
  • Big Blue Is Right On Schedule With Fall 2026 IBM i Tech Refreshes
  • Guru: Environment Variables – Global Variables Wearing Hiking Boots
  • A New IBM i Entity Takes Shape: Power By Fortra
  • IBM i PTF Guide, Volume 28, Number 38

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle