├── docs
├── NO_GC.gif
├── COPY_GC.gif
├── MARK_SWEEP_GC.gif
├── REF_COUNT_GC.gif
├── MARK_COMPACT_GC.gif
├── step-by-step.html
├── engine.js
└── frames.js
├── data
├── dkp.log-small
└── dkp.log-big
├── .gitignore
├── Makefile
├── reference
├── dkp.rb
├── dkp.scala
└── dkp-bad.rb
├── LICENSE
├── README.md
└── dkp.cc
/docs/NO_GC.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenfox/gc-viz/HEAD/docs/NO_GC.gif
--------------------------------------------------------------------------------
/docs/COPY_GC.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenfox/gc-viz/HEAD/docs/COPY_GC.gif
--------------------------------------------------------------------------------
/docs/MARK_SWEEP_GC.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenfox/gc-viz/HEAD/docs/MARK_SWEEP_GC.gif
--------------------------------------------------------------------------------
/docs/REF_COUNT_GC.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenfox/gc-viz/HEAD/docs/REF_COUNT_GC.gif
--------------------------------------------------------------------------------
/data/dkp.log-small:
--------------------------------------------------------------------------------
1 | 5,Jo,Trash
2 | -1,Jo,Candy
3 | 5,Al,Yard
4 | -3,Jo,Game
5 | -1,Al,Candy
6 |
--------------------------------------------------------------------------------
/docs/MARK_COMPACT_GC.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenfox/gc-viz/HEAD/docs/MARK_COMPACT_GC.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files
2 | *.slo
3 | *.lo
4 | *.o
5 | *.obj
6 |
7 | # Compiled Dynamic libraries
8 | *.so
9 | *.dylib
10 | *.dll
11 |
12 | # Compiled Static libraries
13 | *.lai
14 | *.la
15 | *.a
16 | *.lib
17 |
18 | # Executables
19 | *.exe
20 | *.out
21 | *.app
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | #ALGO=NO_GC
3 | #ALGO=REF_COUNT_GC
4 | ALGO=MARK_SWEEP_GC
5 | #ALGO=MARK_COMPACT_GC
6 | #ALGO=COPY_GC
7 |
8 | $(ALGO).gif: dkp.exe
9 | ./dkp.exe data/dkp.log-big > frames.js
10 | rm -f $(ALGO).gif
11 | convert -loop 1 -delay 3 *.xpm $(ALGO).gif
12 | mv -f *.xpm raw
13 |
14 | dkp.exe: Makefile dkp.cc
15 | g++ -D$(ALGO)=1 -o dkp.exe dkp.cc
16 |
--------------------------------------------------------------------------------
/reference/dkp.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | def dkp_log
4 | File.foreach("data/dkp.log-big").map { |line|
5 | amount, person, thing = line.strip.split(",")
6 | [ amount.to_i, person, thing ]
7 | }
8 | end
9 |
10 | standings = dkp_log.group_by { |trans| trans[1] }.map { |person, history|
11 | [ person, history.reduce(0) { |sum, trans| sum + trans[0] } ]
12 | }.sort { |a, b| b[1] <=> a[1] }
13 |
14 | puts standings.to_json
15 |
--------------------------------------------------------------------------------
/reference/dkp.scala:
--------------------------------------------------------------------------------
1 |
2 | val standings = scala.io.Source.fromFile(new java.io.File("data/dkp.log-big"))
3 | .getLines()
4 | .map(line => {
5 | val Array(amount, person, thing) = line.split(',')
6 | (amount.toInt, person, thing)
7 | })
8 | .toSeq
9 | .groupBy(trans => trans._2)
10 | .toSeq
11 | .map(person => {
12 | person._1 -> person._2.foldLeft(0)((sum, trans) => sum + trans._1)
13 | })
14 | .sortWith((a, b) => b._2 < a._2)
15 |
16 | println(standings)
17 |
--------------------------------------------------------------------------------
/data/dkp.log-big:
--------------------------------------------------------------------------------
1 | 10,Mal,Temple of the Jade Serpent
2 | 10,Wash,Temple of the Jade Serpent
3 | 10,Zoe,Temple of the Jade Serpent
4 | 10,Jayne,Temple of the Jade Serpent
5 | 10,Kaylee,Temple of the Jade Serpent
6 | -3,Mal,Pistol
7 | -3,Wash,Hawaiian Shirt
8 | -8,Jayne,Grenade Launcher
9 | -1,Kaylee,Wrench
10 | 10,Zoe,Shando-Pan Monastery
11 | 10,Wash,Shando-Pan Monastery
12 | 10,Mal,Shando-Pan Monastery
13 | 10,Jayne,Shando-Pan Monastery
14 | 10,Shepherd,Shando-Pan Monastery
15 | -2,Wash,Cargo Shorts
16 |
--------------------------------------------------------------------------------
/reference/dkp-bad.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | # This program is almost identical to dkp.rb, but because
4 | # a large temporary result is assigned to a variable, it
5 | # becomes a live GC root and prevents it from becoming
6 | # garbage.
7 |
8 | # Functional style avoids temporary variables partly to
9 | # avoid this unnecessary capture. Garbage is generated like
10 | # crazy in functional style, but it usually performs fine
11 | # due to good GC algorithms that scale with live data.
12 |
13 | dkp_log = File.foreach("data/dkp.log-big").map { |line|
14 | amount, person, thing = line.strip.split(",")
15 | [ amount.to_i, person, thing ]
16 | }
17 |
18 | standings = dkp_log.group_by { |trans| trans[1] }.map { |person, history|
19 | [ person, history.reduce(0) { |sum, trans| sum + trans[0] } ]
20 | }.sort { |a, b| b[1] <=> a[1] }
21 |
22 | puts standings.to_json
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ken Fox
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/step-by-step.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GC Animation
5 |
6 |
11 |
12 |
13 | GC
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | gc-viz
2 | ======
3 |
4 | Animated visualizations of several garbage collection algorithms.
5 |
6 | ```
7 | make
8 | open MARK_SWEEP_GC.gif
9 | ```
10 |
11 | The GIF output requires ImageMagick installed. Edit the Makefile to
12 | choose a different algorithm. If you add more data to the sample,
13 | you'll probably have to increase the GC heap size. This is just a toy
14 | after all!
15 |
16 | The interesting thing here is the GC algorithm animations, but in
17 | order to excercise the GC, I had to create a small sample program.
18 | The `reference` directory contains Ruby and Scala implementations
19 | of the sample program. The `dkp.cc` that generates the visualizations
20 | implements similar logic with the exception that it has the world's
21 | worst sort.
22 |
23 | Here are some notes from one of my talks on GC. I highly recommend
24 | the book _Garbage Collection: Algorithms for Automatic Dynamic Memory
25 | Management_ by Jones and Lins. I haven't read Jones' newer book.
26 | You can also find excellent overviews by Googling "GC algorithm survey";
27 | Paul Wilson's was very useful to me, but there should be newer surveys
28 | available.
29 |
30 | What Is Garbage Collection?
31 | ===========================
32 |
33 | - automatic
34 | - resource - usually memory, but practical for any storage
35 | - management
36 |
37 | - very old technology, general purpose ideas
38 | - misunderstanding causes problems, e.g. "map of weak refs" != cache
39 |
40 | - goals of GC: higher level code, uncouple systems, improve performance
41 |
42 | - different kinds of memory: unintuitive and getting worse:
43 | - L1 cache 1ns
44 | - L2 cache 10ns
45 | - main memory 100ns
46 |
47 | Most programmers are well into the phase of computing where we
48 | depend on the compiler and run-time for memory management just as
49 | we depend on the compiler for code generation. Highly constrained
50 | devices with small memories and/or hard real-time guarantees are
51 | still a problem for GC.
52 |
53 | Terminology
54 |
55 | - root set: active variables (and machine stuff: stack, cpu registers)
56 | - live set: reachable from root set
57 | - garbage: everything else
58 |
59 | Algorithms!
60 | ===========
61 |
62 | ## Free At Exit, aka There's Plenty of RAM
63 |
64 | `NO_GC` option in the Makefile.
65 |
66 | - simplest possible system
67 | - best concurrency (only allocator)
68 | - reasonable for small programs
69 | - definition of "small" increases as technology improves
70 | - no destructors
71 |
72 | ## Reference Counting
73 |
74 | `REF_COUNT_GC` option in the Makefile.
75 |
76 | - 1960
77 | - synchronous - destructors useful
78 | - accidentally ammortized (but long pauses possible)
79 | - simple concurrency
80 | - possible to retrofit
81 |
82 | - expensive in cpu
83 | - needs extra word per object to hold counts
84 | - no cycles
85 | - complicated api and/or leaky abstraction
86 | - expensive allocator (fragmentation, locality)
87 | - which allocator doesn't matter (time efficient slab allocator)
88 | - threading problems (mutating ref counts - no read-only data!, destructor runs on random thread)
89 |
90 | - iOS, file systems
91 |
92 | - extensions:
93 | - deferred ref count
94 | - deferred free
95 | - ref count tables
96 | - N bit (including 1 bit) ref counts
97 |
98 | ## Mark Sweep
99 |
100 | `MARK_SWEEP_GC` option in the Makefile.
101 |
102 | - 1960
103 | - traversal required
104 | - simple
105 | - destructors easy, but delayed
106 | - conservative option (traversal can be approximated)
107 | - possible to retrofit
108 |
109 | - asynch
110 | - expensive allocator (fragmentation, locality)
111 | - complicated concurrency (multi-color)
112 |
113 | - Lua, Flash, Ruby
114 |
115 | - extensions:
116 | - deferred free
117 | - mark tables (examine multiple objects at once)
118 |
119 | ## Mark Compact
120 |
121 | `MARK_COMPACT_GC` option in the Makefile.
122 |
123 | - 1964
124 | - precise traversal required
125 | - simple
126 | - minimum total memory usage
127 | - super cheap allocator ("bump" allocator with only a few instructions)
128 | - moving objects difficult to retrofit
129 |
130 | - needs 3 passes, but can trade memory for performance
131 | - very complicated concurrency (multi-color, barriers)
132 |
133 | - general algorithm that can make other problems easier
134 | - example: fair random row selection
135 |
136 | ```
137 | first pass through the database compacts rows so that
138 | there are no gaps in position, e.g. 1, 2, 3, ...
139 |
140 | select * where position > random() order by position limit 1
141 | ```
142 |
143 | ## Copy
144 |
145 | `COPY_GC` option in the Makefile.
146 |
147 | - 1962
148 | - precise traversal required
149 | - simplest
150 | - super cheap allocator
151 | - work proportional to live data (garbage doesn't matter!)
152 |
153 | - semi-spaces
154 | - no destructors - finalize should not be used
155 | - very complicated concurrency (multi-color, barriers)
156 | - moving objects difficult to retrofit
157 |
158 | - common degenerate case: per-transaction pool, delete when done
159 |
160 | - most useful gc algorithm whenever you have little live data
161 | - non-memory example: web session storage deletion on Amazon SimpleDB
162 |
163 | create new and old domains
164 | your server reads from new and faults old into it
165 | nightly: delete the old domain, create new one, and flip
166 |
167 | ## Generational, Ephemeral and more
168 |
169 | - 1984
170 | - hypothesis: most objects die young
171 | - chain together copy collectors
172 | - oldest generation can use a different gc method
173 |
174 | - inter-generational references suck
175 | - non-intuitive performance (garbage is cheap, reuse is expensive)
176 |
177 | - foundation for all advanced modern gc
178 |
--------------------------------------------------------------------------------
/docs/engine.js:
--------------------------------------------------------------------------------
1 | var density = 'ultra';
2 |
3 | var legend_canvas = document.getElementById('legend');
4 | var legend_ctx = legend_canvas.getContext('2d');
5 | legend_ctx.lineWidth = 1;
6 | legend_ctx.strokeStyle = "#ccccee";
7 | if (legend_ctx.setLineDash) {
8 | legend_ctx.setLineDash([2,2]);
9 | }
10 |
11 | var memory_canvas = document.getElementById('memory');
12 | var memory_ctx = memory_canvas.getContext('2d');
13 | memory_ctx.textAlign = "end";
14 |
15 | var animation_canvas = document.getElementById('animation');
16 | var animation_ctx = animation_canvas.getContext('2d');
17 | animation_ctx.lineJoin = 'round';
18 |
19 | var drawing_canvas = document.getElementById('drawing');
20 | var drawing_ctx = drawing_canvas.getContext('2d');
21 | drawing_ctx.lineJoin = 'round';
22 |
23 | var word_height;
24 | var word_width;
25 | var label_offset_x;
26 | var label_offset_y;
27 |
28 | if (density == 'low') {
29 | word_height = 30;
30 | word_width = 80;
31 | label_offset_x = word_width - 8;
32 | label_offset_y = word_height - 8;
33 | memory_ctx.font = "24px Helvetica,sans-serif;"
34 | animation_ctx.lineWidth = 3;
35 | }
36 | else if (density == 'high') {
37 | word_height = 16;
38 | word_width = 40;
39 | label_offset_x = word_width - 2;
40 | label_offset_y = word_height - 3;
41 | memory_ctx.font = "14px Helvetica,sans-serif;"
42 | animation_ctx.lineWidth = 2;
43 | }
44 | else if (density == 'ultra') {
45 | word_height = 12;
46 | word_width = 30;
47 | label_offset_x = word_width - 2;
48 | label_offset_y = word_height - 2;
49 | memory_ctx.font = "10px Helvetica,sans-serif"
50 | animation_ctx.lineWidth = 2;
51 | }
52 | else { // default medium density
53 | word_height = 20;
54 | word_width = 50;
55 | label_offset_x = word_width - 4;
56 | label_offset_y = word_height - 4;
57 | memory_ctx.font = "16px Helvetica,sans-serif;"
58 | animation_ctx.lineWidth = 3;
59 | }
60 |
61 |
62 | var word_half_height = Math.floor(word_height / 2);
63 | var word_half_width = Math.floor(word_width / 2);
64 | var memory_width = Math.floor(memory_canvas.width / word_width)
65 |
66 | function addressToPoint(addr) {
67 | var y = Math.floor(addr / memory_width);
68 | var x = addr - y * memory_width;
69 | return [x, y];
70 | }
71 |
72 | function drawGuides() {
73 | var end_x = legend_canvas.width;
74 | var end_y = legend_canvas.height;
75 | for (var x = 0.5; x <= end_x; x += word_width) {
76 | legend_ctx.beginPath();
77 | legend_ctx.moveTo(x, 0.5);
78 | legend_ctx.lineTo(x, end_y);
79 | legend_ctx.closePath();
80 | legend_ctx.stroke();
81 | }
82 | for (var y = 0.5; y <= end_y; y += word_height) {
83 | legend_ctx.beginPath();
84 | legend_ctx.moveTo(0.5, y);
85 | legend_ctx.lineTo(end_x, y);
86 | legend_ctx.closePath();
87 | legend_ctx.stroke();
88 | }
89 | }
90 |
91 | var mem_content = [
92 | ":nil"
93 | ];
94 |
95 | function drawWord(addr) {
96 | var p = addressToPoint(addr);
97 | var x = p[0] * word_width + 1;
98 | var y = p[1] * word_height + 1;
99 |
100 | memory_ctx.fillStyle = "#222222";
101 | memory_ctx.fillRect(x, y, word_width - 1, word_height - 1);
102 |
103 | var cell = mem_content[addr].toString();
104 | if (cell) {
105 | if (cell.charAt(0) == "'") {
106 | memory_ctx.fillStyle = "#00ff00";
107 | cell = cell.substring(1);
108 | }
109 | else if (cell.charAt(0) == "=") {
110 | memory_ctx.fillStyle = "#00ff00";
111 | cell = cell.substring(1);
112 | }
113 | else if (cell.charAt(0) == ":") {
114 | memory_ctx.fillStyle = "#ffffff";
115 | cell = cell.substring(1);
116 | }
117 | else {
118 | memory_ctx.fillStyle = "#ffff00";
119 | var c = addressToPoint(mem_content[addr]);
120 | cell = c[1].toString() + "." + c[0];
121 | }
122 |
123 | memory_ctx.fillText(cell, x + label_offset_x, y + label_offset_y);
124 | }
125 | }
126 |
127 | function clearWord(addr) {
128 | var p = addressToPoint(addr);
129 | var x = p[0] * word_width + 1;
130 | var y = p[1] * word_height + 1;
131 |
132 | memory_ctx.fillStyle = "#ffffff";
133 | memory_ctx.fillRect(x, y, word_width - 1, word_height - 1);
134 | }
135 |
136 | function copyWords(to_addr, from_addr, count, subframe) {
137 | if (subframe >= 100) {
138 | for (var i = 0; i < count; ++i) {
139 | drawWord(to_addr + i);
140 | }
141 | }
142 | else {
143 | animation_ctx.fillStyle = "#000000";
144 | animation_ctx.strokeStyle = "#00ff00";
145 |
146 | var percent = subframe / 100;
147 | for (var i = 0; i < count; ++i) {
148 | var from_p = addressToPoint(from_addr + i);
149 | var from_x = from_p[0] * word_width;
150 | var from_y = from_p[1] * word_height;
151 | var from_mid_x = from_x + word_half_width;
152 | var from_mid_y = from_y + word_half_height;
153 |
154 | var to_p = addressToPoint(to_addr + i);
155 | var to_x = to_p[0] * word_width;
156 | var to_y = to_p[1] * word_height;
157 |
158 | var delta_x = Math.round((to_x - from_x) * percent);
159 | var delta_y = Math.round((to_y - from_y) * percent);
160 |
161 | animation_ctx.fillRect(from_x + delta_x, from_y + delta_y,
162 | word_width - 1, word_height - 1);
163 |
164 | if (i == 0) {
165 | animation_ctx.beginPath();
166 | animation_ctx.moveTo(from_mid_x, from_mid_y);
167 | animation_ctx.lineTo(from_mid_x + delta_x, from_mid_y + delta_y);
168 | animation_ctx.stroke();
169 | }
170 | }
171 | }
172 | }
173 |
174 | function blinkWords(list, subframe, color) {
175 | if (subframe < 100 && subframe % 20 != 0) {
176 | animation_ctx.strokeStyle = color;
177 |
178 | for (var i = 1; i < list.length; ++i) {
179 | var p = addressToPoint(list[i]);
180 | var x = p[0] * word_width;
181 | var y = p[1] * word_height;
182 |
183 | animation_ctx.strokeRect(x, y, word_width + 1, word_height + 1);
184 | }
185 | }
186 | }
187 |
188 | drawGuides();
189 | drawWord(0);
190 |
191 | function draw_connection(event) {
192 | var totalOffsetX = 0;
193 | var totalOffsetY = 0;
194 | var currentElement = this;
195 |
196 | do {
197 | totalOffsetX += currentElement.offsetLeft;
198 | totalOffsetY += currentElement.offsetTop;
199 | }
200 | while (currentElement = currentElement.offsetParent);
201 |
202 | //var x = Math.floor((event.pageX - totalOffsetX) / word_width);
203 | //var y = Math.floor((event.pageY - totalOffsetY) / word_height);
204 | var x = Math.floor(event.layerX / word_width);
205 | var y = Math.floor(event.layerY / word_height);
206 | var addr = y * memory_width + x;
207 |
208 | x = x * word_width;
209 | y = y * word_height;
210 |
211 | drawing_ctx.clearRect(0, 0, drawing_canvas.width, drawing_canvas.height);
212 |
213 | if (mem_content[addr]) {
214 | drawing_ctx.lineWidth = 3;
215 | drawing_ctx.strokeStyle = "#ffffff";
216 | drawing_ctx.strokeRect(x, y, word_width + 1, word_height + 1);
217 |
218 | var cell = mem_content[addr].toString();
219 | var to_x;
220 | var to_y;
221 |
222 | if (cell.charAt(0) >= '0' && cell.charAt(0) <= '9') {
223 | var p = addressToPoint(mem_content[addr]);
224 | to_x = p[0] * word_width;
225 | to_y = p[1] * word_height;
226 | }
227 |
228 | if (to_x || to_y) {
229 | drawing_ctx.lineWidth = 1;
230 | drawing_ctx.beginPath();
231 | drawing_ctx.moveTo(x + word_half_width, y + word_half_height);
232 | drawing_ctx.lineTo(to_x + word_half_width, to_y + word_half_height);
233 | drawing_ctx.stroke();
234 | }
235 | }
236 | }
237 |
238 | drawing_canvas.addEventListener("mousedown", draw_connection, false);
239 |
240 | var requestAnimationFrame = (function() {
241 | return window.requestAnimationFrame ||
242 | window.webkitRequestAnimationFrame ||
243 | window.mozRequestAnimationFrame ||
244 | window.oRequestAnimationFrame ||
245 | window.msRequestAnimationFrame ||
246 | function(callback) {
247 | window.setTimeout(callback, 1000 / 60);
248 | };
249 | })();
250 |
251 | var animation_running = false;
252 | var frame = 0;
253 | var subframe = 0;
254 | var frame_count = frame_content.length;
255 |
256 | function toggle_animation() {
257 | var button = document.getElementById('run_button');
258 | animation_running = !animation_running;
259 | if (animation_running) {
260 | button.innerHTML = "Pause";
261 | requestAnimationFrame(animate);
262 | }
263 | else {
264 | button.innerHTML = "Continue";
265 | }
266 | var status = document.getElementById('run_status');
267 | status.innerHTML = "";
268 | }
269 |
270 | function pause_animation(message) {
271 | var button = document.getElementById('run_button');
272 | animation_running = false;
273 | button.innerHTML = "Continue";
274 | var status = document.getElementById('run_status');
275 | status.innerHTML = message;
276 | }
277 |
278 | function stop_animation() {
279 | var button = document.getElementById('run_button');
280 | frame = 0;
281 | animation_running = false;
282 | button.innerHTML = "Start";
283 | var status = document.getElementById('run_status');
284 | status.innerHTML = "";
285 | var bp_msg = document.getElementById('bp_msg');
286 | bp_msg.innerHTML = "";
287 | }
288 |
289 | function animate(timestamp) {
290 | if (animation_running && frame < frame_count) {
291 | requestAnimationFrame(animate);
292 | animation_ctx.clearRect(0, 0, animation_canvas.width, animation_canvas.height);
293 |
294 | if (subframe == 0) {
295 | if (frame_content[frame][0] == 'box') {
296 | mem_content[frame_content[frame][1]] = frame_content[frame][2];
297 | drawWord(frame_content[frame][1]);
298 | }
299 | else if (frame_content[frame][0] == 'set') {
300 | mem_content[frame_content[frame][1]] = frame_content[frame][2];
301 | drawWord(frame_content[frame][1]);
302 | }
303 | else if (frame_content[frame][0] == 'alloc') {
304 | for (var i = 0; i < frame_content[frame][2]; ++i) {
305 | mem_content[frame_content[frame][1] + i] = "";
306 | drawWord(frame_content[frame][1] + i);
307 | }
308 | }
309 | else if (frame_content[frame][0] == 'free') {
310 | for (var i = 0; i < frame_content[frame][2]; ++i) {
311 | mem_content[frame_content[frame][1] + i] = "";
312 | clearWord(frame_content[frame][1] + i);
313 | }
314 | }
315 | else if (frame_content[frame][0] == 'ref_count') {
316 | }
317 | else if (frame_content[frame][0] == 'bp') {
318 | var bp_msg = document.getElementById('bp_msg');
319 | bp_msg.innerHTML = frame_content[frame][1];
320 | }
321 | else if (frame_content[frame][0] == 'roots') {
322 | blinkWords(frame_content[frame], 1, "#ff0000");
323 | pause_animation("root set");
324 | }
325 | else if (frame_content[frame][0] == 'live') {
326 | blinkWords(frame_content[frame], 1, "#ff0000");
327 | pause_animation("live set");
328 | }
329 | else if (frame_content[frame][0] == 'stop') {
330 | stop_animation();
331 | }
332 | else {
333 | // begin a subframe animation
334 | subframe += 10;
335 | if (frame_content[frame][0] == 'copy') {
336 | for (var i = 0; i < frame_content[frame][3]; ++i) {
337 | mem_content[frame_content[frame][1] + i] = mem_content[frame_content[frame][2] + i];
338 | }
339 | copyWords(frame_content[frame][1], frame_content[frame][2], frame_content[frame][3], subframe);
340 | }
341 | }
342 | if (subframe == 0) {
343 | ++frame;
344 | }
345 | }
346 | else {
347 | subframe += 10;
348 | if (frame_content[frame][0] == 'copy') {
349 | copyWords(frame_content[frame][1], frame_content[frame][2], frame_content[frame][3], subframe);
350 | }
351 | else if (frame_content[frame][0] == 'roots') {
352 | blinkWords(frame_content[frame], subframe, "#ff0000");
353 | }
354 | else if (frame_content[frame][0] == 'live') {
355 | blinkWords(frame_content[frame], subframe, "#ff0000");
356 | }
357 | if (subframe >= 100) {
358 | subframe = 0;
359 | ++frame;
360 | }
361 | }
362 | }
363 | }
364 |
--------------------------------------------------------------------------------
/dkp.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * This file and the rest of this project are under the MIT License.
3 | *
4 | * This program contains toy implementations of several different
5 | * garbage collector algorithms instrumented to produce nice
6 | * visualizations of how the algorithms work. Many corners were cut to
7 | * simplify the code so they are neither general purpose nor
8 | * efficient. It's not all smoke and mirrors, but where smoke and
9 | * mirrors worked, that's what I used.
10 | *
11 | * If you are trying to understand GC algorithms, it's best to read a
12 | * good introductory text such as the Jones Lins book and only look at
13 | * the visualizations this program generates until you understand the
14 | * algorithm.
15 | *
16 | * Ken Fox
17 | * August 2014
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include