Chuyện gì đang xảy ra trong ứng dụng Ruby của bạn?
Bài đăng này đã không được cập nhật trong 7 năm
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_methods
trong 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}\n"
klass.send(:aliased_methods).each do |m1, m2|
methods << "#{m1.to_s.ljust(15)} #{m2}\n"
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.
All rights reserved