Chuyện gì đang xảy ra trong ứng dụng Ruby của bạn?
Bạn sẽ làm gì nếu như bạn muốn biết những điều đang xảy ra trong ứng dụng Ruby của bạn? Trong Ruby chúng ta không có các tools như Java, nhưng chúng ta có module ObjectSpace, module này sẽ cung cấp cho bạn một số thông tin về trạng thái hiện tại của ứng dụng của bạn. Đếm số lượng object Sử ...
Bạn sẽ làm gì nếu như bạn muốn biết những điều đang xảy ra trong ứng dụng Ruby của bạn?
Trong Ruby chúng ta không có các tools như Java, nhưng chúng ta có module ObjectSpace, module này sẽ cung cấp cho bạn một số thông tin về trạng thái hiện tại của ứng dụng của bạn.
Đếm số lượng object
Sử dụng ObjectSpace bạn có thể biết được những objects nào đang sống trong program của bạn.
Vậy một object được coi là vẫn còn sống có nghĩa gì? Một object là còn sống có nghĩa là nó vẫn còn ràng buộc nào đó trỏ đến nó. Một ràng buộc ở đây đơn giản là các để access đến object đó, nhưn là một variable hoặc một constant nào đó. Nếu object không thể tiếp cận được thì nó có nghĩa là an toàn khi loại bỏ chúng khỏi bộ nhớ.
Ví dụ:
# Biến 'name' giữ một liên kết ràng buộc tới string 'Dave'. name = 'Dave' # Biến 'name' bây giờ có trỏ đến 'John'. # => 'Dave' không còn liên kết nào tới biến 'name' nữa name = 'John'
Bây giờ chúng ta hãy cùng xem ví dụ về ObjectSpace module:
require 'objspace' # This is valid Ruby syntax, but doesn't work on irb/pry ObjectSpace .each_object .inject(Hash.new 0) { |h,o| h[o.class] += 1; h } .sort_by { |k,v| -v } .take(10) .each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" } # Copy & paste version (use this for irb/pry) ObjectSpace.each_object.inject(Hash.new 0) { |h,o| h[o.class] += 1; h }.sort_by { |k,v| -v }.take(10).each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" }
Nó sẽ trả về top 10 classes mà có số lượng lớn nhất.
Count Class ------------------------- 215853 String 64145 Array 53819 RubyVM::InstructionSequence 24337 Hash 14831 Proc 13539 RubyVM::Env 5575 Set 5430 Class 5102 ActionDispatch::Journey::Nodes::Cat 4685 Regexp
Nếu bạn nghi ngờ memory bị leak bạn có thể log data mỗi giờ thì sẽ tìm thấy được một số objects nó liên tục tăng và không có dấu hiệu giảm xuống (vì bản thân câu lệnh trên cũng đã tạo ra object mới.)
Fun with Objects
Khi bạn sử dụng ObjectSpace thì bạn thực sự có quyền truy cập vào một object thực tế, không chỉ là các thông tin về chúng, vì vậy bạn có thể làm một số điều thú vị với chúng như là in giá trị của strings hoặc là in path của tất cả File objects.
ObjectSpace .each_object(String) .sort_by { |s| s.size } .each { |s| p s }
=> Lệnh trên sẽ in tất cả strings trong bộ nhớ, sắp xếp theo kích thước. Bạn sẽ nhận thấy rằng sẽ có rất nhiều string mà không phải do bạn tạo ra, chúng được tạo ra bởi Ruby interpreter.
Hầu như sẽ không được dùng trong thực tế nhiều. CHúng thường được sử dụng để thống kê và debugging về ứng dụng của bạn.
Object Memory Size
Một điều bạn có thể làm là sử dụng ObjectSpace.memsize_of để tìm kích thước bộ nhớ của một đối tượng cụ thể:
ObjectSpace.memsize_of("a" * 100) => 141 ObjectSpace.memsize_of("a" * 22) => 40
Bạn cần phải chú ý một điều là: Kích thước trả về là không đầy đủ (hiểu một cách là không chính xác toàn bộ). Cái này chỉ được xem như là một gợi ý. Đặc biệt kích thước của T_DATA có thể không chính xác.
Nếu bạn thử sử dụng method này với các loại object khác nhau bạn sẽ thấy được một điều thú vị rằng Fixnum luôn luôn return 0
ObjectSpace.memsize_of(1) => 0 ObjectSpace.memsize_of(1000) => 0
Và nguyên nhân là do Ruby không tạo một object Fixnum bên trong nó, bạn có thể tìm hiểu thêm về nó ở đây. Một vài điều thú vị khác như:
ObjectSpace.memsize_of("A" * 22) => 40 ObjectSpace.memsize_of("A" * 23) => 40 ObjectSpace.memsize_of("A" * 24) => 65
Tại sao lại có một bước nhảy lớn như vậy từ 23-24 kí tự? Nhìn kết quả trên ta có thể thấy rằng Ruby có một sự tối ưu hóa cho các chuỗi string nhỏ hơn 24 kí tự, đó là lý do tại sao ta lại có một sự khác biệt khi lấy size của 24 kí tự và nhỏ hơn nó. Bạn có thể thấy điều này chi tiết hơn trong bài viết này của Pat Shaughnessy.
Finding Aliased Methods
Hãy cùng xem mã code dưới đây
class Module def aliased_methods instance_methods(false) .group_by{|m| instance_method(m)} .map(&:last) .keep_if{|symbols| symbols.length > 1} end end
Tôi đã tìm thấy nó trên Stackoverflow. Nó định nghĩa một phương thức aliased_methodstrong Module class, sử dụng phương thức instance_methods để get tất cả danh sách các instance methods được định nghĩa ở trong class.
Dưới đây là phần còn lại của code, nó sẽ tạo một array của tất cả các class names mà có ít nhất một object còn sống, sau đó nó sẽ gọi aliased_methods cho mọi class và in ra kết quả
objects = ObjectSpace.each_object.map(&:class).uniq objects.each do |klass| methods = "#{'-'*20}#{klass}#{'-'*20} " klass.send(:aliased_methods).each do |m1, m2| methods << "#{m1.to_s.ljust(15)} #{m2} " end puts methods end
Nó sẽ trả về kết quả giống như sau:
--------------------String-------------------- == === [] slice length size succ next succ! next! to_s to_str concat << intern to_sym start_with? starts_with? end_with? ends_with? camelize camelcase titleize titlecase to_json_with_active_support_encoder to_json --------------------Class-------------------- superclass_delegating_accessor superclass_delegating_accessor_with_deprecation --------------------Array-------------------- inspect to_default_s to_s to_formatted_s [] slice << append unshift prepend length size empty? blank? find_index index collect map collect! map! to_json_with_active_support_encoder to_json --------------------Proc-------------------- call [] ......
Kết luận
Hy vọng bạn sẽ học được thêm những điều mới lạ từ ObjectSpace module.