Some Rules for Safer Java Programming
- Can Java leak memory? Programmers can.
- How Plentiful Is Memory?
- Conclusion
Sometime around 1997, a programmer colleague of mine was wrestling with what seemed like an intractable C++ bug. When he asked me for advice, I suggested, "You've probably exceeded the boundary of an array." This was (and still is) one of the most common C/C++ errors. He was amazed when a code check revealed that this was indeed the problem! Far from displaying god-like omniscience, this was just a case of the programming languages of the day requiring abstract rules and guidelines such as the one described. In fact, this conversational exchange was probably repeated all over the world by C++ developers! If that suggestion hadn't worked, I'd have suggested checking for other errors such as null pointer access, erroneous file I/O access, and so on. If none of those worked, I'd have suggested running the code with a debugger. It's all about rules!
Times and technologies have changed. The Java Runtime Environment now throws an exception if you exceed the boundary of an array. So, if you're guilty of this particular sin (as we all have been), you'll get to hear about it quickly enough! If you forget to handle the exception, your program is aborted. The reality is this: Each technology provides its own fertile ground for error, and Java is no exception. In this article, I look at a few issues that that can cause serious problems in Java code, and outline a few handy techniques for avoiding such problems.
Can Java leak memory? Programmers can.
A common Java misconception is that you don't need to worry about memory at all—the garbage collector takes care of all that stuff! Not necessarily. It's relatively easy to write Java code that allocates large amounts of memory and then forget to make that code go out of scope. This is a type of inadvertent memory leak, and is illustrated in Listing 1.
Listing 1 A Java Memory Leak
public class MemoryLeak { public static void main(String[] args) { Scanner keyboard = new Scanner(System.in); int keepGoing = 0; System.out.println("Please enter a value for keepGoing " + keepGoing); keepGoing = keyboard.nextInt(); System.out.println("New value for keepGoing is " + keepGoing); if (keepGoing != 0) { System.out.println("Continuing program. Value of keepGoing " + keepGoing); int[] aBiggishArray = new int[5000]; System.out.println("Allocated an array of size " + aBiggishArray.length); // LOTS MORE CODE HERE // DON’T NEED aBiggishArray AFTER THIS // BUT, MEMORY FOR aBiggishArray IS STILL ALLOCATED } else { System.out.println("Exiting program. Value of keepGoing " + keepGoing); } } }
In Listing 1, I allocate a big array called aBiggishArray, and I use it for a few lines. At this stage, I no longer need the array, so I then forget about it. Until the if statement ends, the array object remains in scope locked in memory, impervious to the demands of the garbage collector. This might be a slightly contrived example, but it does illustrate that code logic may inadvertently lead to memory leakage. Of course, once the object aBiggishArray goes out of scope, the memory is released. Perhaps, the important question is: Do we really need to worry so much about resources such as memory?