unix.ba | text • research • links • about |
Crashing programs has always been fun, but it can also be a great means for learning a few things about memory organization and allocation. I find it especially useful when speaking with young programmers about physical limitations of a computer.
Let me clarify: I am not interested in demonstrating an illegal memory access which results in a SIGSEGV or a buffer overflow. Rather, I wish to show to a young programmer what uncontrolled dynamic memory allocation (e.g. a memory leak) can do to a program, even if that program is written in an innocent-looking language like Scratch or Snap!.
One easy way to simulate a memory leak and exhaust a computer's memory using Scratch 1.4 is to write and execute a simple program such as Program 1 shown below. Notice that this program does not introduce a true memory leak, but only demonstrates the effects of one.
Program 1 - A program with an exponentially growing string
Program 1 begins by assigning the value of “A” to the variable leaky_var. Inside the loop, leaky_var is first set to “AA”, then “AAAA”, then “AAAAAAAA” and so on. Each loop iteration doubles the length of the content of leaky_var. This makes sure that all your memory gets exhausted pretty quickly, no matter how much of it you have. That's the beauty of exponential growth and this example is a great way to teach it to young people.
If you are running Linux, the OOM Killer will take care of killing Scratch as soon as it comes close to exhausing all of your RAM. In my case, I also had to make sure that the value of leaky_var is visible on the stage (i.e. the check box next to the variable name is “checked” ). Otherwise, Squeak would quickly detect that the program is using too much memory and would pop up a message box (Figure 1). In this case, crashing Scratch was not possible since each click of the “Proceed” button brought the message back.
Figure 1 - The message displayed when Scratch 1.4 allocates too much memory
To illustrate what was happening to my computer's memory during the execution of Program 1, I recorded the graph shown in Figure 2. This graph shows how the amount of free memory was decreasing until it reached the point at which Scratch crashed.
Figure 2 - Amount of free memory during the execution of Program 1
The program execution began around second 10 and lasted until second 85, where the entire memory was almost exhausted. This is the point at which the Linux kernel killed Scratch and reclaimed all of its memory. Interestingly, this entire execerise also killed several Chrome windows, which is why we can see a significant difference in the amount of free memory before and after the experiment.
When it comes to crashing Scratch 2, there is bad news and good news. The bad news is that Program 1 doesn't work. Namely, Scratch 2 has a limitation of 10240 characters per string, so we cannot grow a string beyond that limit. The good news is that we can crash Scratch 2 much faster than Scratch 1.4 and nicely demonstrate the violent nature of exponential growth.
Program 2 - A Scratch 2 program with an exponentially growing list
Program 2 works in Scratch 2. Notice that it is based on the same principle as Program 1, with the difference that here a list leaky_list is used instead of a variable leaky_var. Each iteration of the loop appends the entire list to itself, thereby doubling the amount of memory occupied by the list. The end result is the same - memory is exhausted and Scratch eventually gets killed (Figure 3).
Figure 3 - Flash container running Scratch 2 crashed by Program 2
Notice that I placed a 2-second delay inside the loop body. Without it, the program would eat all the memory within 2-3 seconds, which wouldn't allow me to record the nice graph shown below (Figure 4):
Figure 4 - Amount of free memory during the execution of Program 2
Here also the execution began around second 10 and lasted until about second 80. The shape of the function resembles an exponential function, though the graph is stretched horizontally due to the delays inside the loop.
Snap! also imposes a limit on the length of a string, so in order to crash it, we must look for a solution with lists, similar to the one given in Program 2.
Program 3 - A Snap! program with an exponentially growing list
Even though the syntax of Snap! is a bit different than that of Scratch 2, it is clear that Program 2 and Program 3 do essentially the same thing. Notice that in Program 3 I also used the warp construct to make Snap! execute as fast as possible. After running Program 3 for some time, Snap! crashed with the message shown in Figure 5.
Figure 5 - Chrome window crashed by Snap!
Despite using warp, Snap! dragged like a snail and took twice the time needed by Scratch 1.4 to reach the point of crashing. The graph in Figure 6 shows free memory during the execution of Program 3.
Figure 6 - Amount of free memory during the execution of Program 3
If you are a teacher using Scratch or Snap! to teach young people programming, I encourage you to try the above examples with your students. You can begin by showing them the code for one of the above programs, then let them figure out what will happen when the program is executed. Instead of running the program yourself, let the students run it on their computers to have them experience the crash. This is an excellent point to start a discussion on several concepts including dynamic memory allocation, memory leaks and exponential growth. Finally, you may offer extra credits to those students who discover alternative ways for crashing Scratch or Snap!.
• • •