├── README └── gc_stats.rb /README: -------------------------------------------------------------------------------- 1 | Basic middleware to help developers track their memory usage. 2 | 3 | DO NOT USE IN PRODUCTION 4 | Currently only tested on Ruby 1.9 and no support guaranteed 5 | 6 | Output example: 7 | 8 | GC run, previous cycle was 255 requests ago. 9 | 10 | GC 40 invokes. 11 | Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms) 12 | 1 1.267 3094640 4063232 101432 14.47700000000007314327 13 | 2 1.391 3088480 4063232 101432 13.95699999999999718625 14 | 3 1.514 3091160 4063232 101432 13.84699999999994268762 15 | 4 1.643 3093400 4063232 101432 14.65799999999983782573 16 | 5 1.771 3094800 4063232 101432 15.47099999999979047516 17 | 6 1.899 3089200 4063232 101432 14.96900000000001007550 18 | 7 2.028 3091600 4063232 101432 17.90399999999969793407 19 | 8 2.164 3093320 4063232 101432 15.38599999999989975663 20 | 9 2.298 3091440 4063232 101432 15.29500000000005854872 21 | 10 2.432 3089800 4063232 101432 16.75899999999996836664 22 | 11 2.570 3093280 4063232 101432 14.70199999999977080734 23 | 24 | ## 23900 freed objects. ## 25 | [60%] 14414 freed strings. 26 | [12%] 2927 freed arrays. 27 | [9%] 2268 freed big numbers. 28 | [2%] 564 freed hashes. 29 | [1%] 373 freed objects. 30 | [5%] 1351 freed parser nodes (eval usage). 31 | 32 | 33 | or: 34 | 35 | [GC Stats] 146 new allocated objects. -------------------------------------------------------------------------------- /gc_stats.rb: -------------------------------------------------------------------------------- 1 | # Basic middleware to help developers track their memory usage 2 | # DO NOT USE IN PRODUCTION 3 | # Currently only tested on Ruby 1.9 and no support guaranteed 4 | 5 | # Output example: 6 | # 7 | # GC run, previous cycle was 255 requests ago. 8 | # 9 | # GC 40 invokes. 10 | # Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms) 11 | # 1 1.267 3094640 4063232 101432 14.47700000000007314327 12 | # 2 1.391 3088480 4063232 101432 13.95699999999999718625 13 | # 3 1.514 3091160 4063232 101432 13.84699999999994268762 14 | # 4 1.643 3093400 4063232 101432 14.65799999999983782573 15 | # 5 1.771 3094800 4063232 101432 15.47099999999979047516 16 | # 6 1.899 3089200 4063232 101432 14.96900000000001007550 17 | # 7 2.028 3091600 4063232 101432 17.90399999999969793407 18 | # 8 2.164 3093320 4063232 101432 15.38599999999989975663 19 | # 9 2.298 3091440 4063232 101432 15.29500000000005854872 20 | # 10 2.432 3089800 4063232 101432 16.75899999999996836664 21 | # 11 2.570 3093280 4063232 101432 14.70199999999977080734 22 | # 23 | # ## 23900 freed objects. ## 24 | # [60%] 14414 freed strings. 25 | # [12%] 2927 freed arrays. 26 | # [9%] 2268 freed big numbers. 27 | # [2%] 564 freed hashes. 28 | # [1%] 373 freed objects. 29 | # [5%] 1351 freed parser nodes (eval usage). 30 | # 31 | # 32 | # or: 33 | # 34 | # [GC Stats] 146 new allocated objects. 35 | 36 | class GCStats 37 | 38 | @@req_since_gc_cycle = 0 39 | 40 | def initialize(app) 41 | GC::Profiler.enable unless GC::Profiler.enabled? 42 | @app = app 43 | end 44 | 45 | def call(env) 46 | before_stats = ObjectSpace.count_objects 47 | response = @app.call(env) 48 | after_stats = ObjectSpace.count_objects 49 | if before_stats[:TOTAL] < after_stats[:TOTAL] 50 | puts "Total objects in memory bumped by #{after_stats[:TOTAL] - before_stats[:TOTAL]} objects." 51 | end 52 | 53 | if before_stats[:FREE] > after_stats[:FREE] 54 | puts "\033[0;32m[GC Stats]\033[1;33m #{before_stats[:FREE] - after_stats[:FREE]} new allocated objects.\033[0m" 55 | @@req_since_gc_cycle += 1 56 | else 57 | report = GC::Profiler.result 58 | GC::Profiler.clear 59 | if report != '' 60 | puts red("\n GC run, previous cycle was #{@@req_since_gc_cycle} requests ago.\n") 61 | puts report 62 | total_freed = after_stats[:FREE] - before_stats[:FREE] 63 | freed_strings = before_stats[:T_STRING] - after_stats[:T_STRING] 64 | freed_arrays = before_stats[:T_ARRAY] - after_stats[:T_ARRAY] 65 | freed_nums = before_stats[:T_BIGNUM] - after_stats[:T_BIGNUM] 66 | freed_hashes = before_stats[:T_HASH] - after_stats[:T_HASH] 67 | freed_objects = before_stats[:T_OBJECT] - after_stats[:T_OBJECT] 68 | freed_nodes = before_stats[:T_NODE] - after_stats[:T_NODE] 69 | 70 | freed_strings_percent = ((freed_strings * 100) / total_freed) 71 | freed_arrays_percent = ((freed_arrays * 100) / total_freed) 72 | freed_nums_percent = ((freed_nums * 100) / total_freed) 73 | freed_hashes_percent = ((freed_hashes * 100) / total_freed) 74 | freed_objects_percent = ((freed_objects * 100) / total_freed) 75 | freed_nodes_percent = ((freed_nodes * 100) / total_freed) 76 | 77 | puts red("\n## #{total_freed} freed objects. ##") 78 | puts red("[#{freed_strings_percent}%] #{freed_strings} freed strings.") 79 | puts red("[#{freed_arrays_percent}%] #{freed_arrays} freed arrays.") 80 | puts red("[#{freed_nums_percent}%] #{freed_nums} freed bignums.") 81 | puts red("[#{freed_hashes_percent}%] #{freed_hashes} freed hashes.") 82 | puts red("[#{freed_objects_percent}%] #{freed_objects} freed objects.") 83 | puts red("[#{freed_nodes_percent}%] #{freed_nodes} freed parser nodes (eval usage).") 84 | 85 | # puts "before objects: #{before_stats.inspect}" 86 | # puts "after objects: #{after_stats.inspect}" 87 | puts "\n------\n" 88 | @@req_since_gc_cycle = 0 89 | end 90 | end 91 | 92 | response 93 | end 94 | 95 | def red(text) 96 | "\033[0;31m#{text}\033[0m" 97 | end 98 | 99 | end --------------------------------------------------------------------------------