Nếu bạn đang thắc mắc những gì đang xảy ra với ứng dụng Ruby của bạn...
Không có công cụ GUI sang trọng nào...
Nhưng chúng ta có module ObjectSpace!
ObjectSpace cung cấp thông tin về trạng thái hiện tại của ứng dụng.
Hãy khám phá cách nó hoạt động.
Đếm đối tượng
Sử dụng ObjectSpace, bạn có thể biết những đối tượng nào hiện đang "sống" trong chương trình.
Đối tượng "sống" nghĩa là gì?
Một đối tượng được coi là sống miễn là nó có bất kỳ tham chiếu nào trỏ đến nó. Tham chiếu chỉ là cách truy cập đối tượng, như một biến hoặc hằng số.
Nếu một đối tượng không thể truy cập được, nghĩa là nó an toàn để bị xóa khỏi bộ nhớ.
Ví dụ:
# Biến 'name' giữ tham chiếu đến chuỗi 'Dave'. name = 'Dave' # Biến 'name' giờ trỏ đến 'John'. # 'Dave' không còn có tham chiếu nào trỏ đến nó. name = 'John'
Hãy xem ví dụ của ObjectSpace trong hành động:
require 'objspace'
# Cú pháp Ruby hợp lệ, nhưng không chạy trên 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}" }
# Phiên bản copy & paste (dùng cho 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}" }
Điều này sẽ in một bảng với số lượng đối tượng cho top-10 lớp.
Count Class ------------------------- 5436 String 315 Class 251 Array 101 Encoding 69 Regexp 45 Hash 26 Module 25 Gem::Version 22 Gem::StubSpecification::StubLine 22 Gem::StubSpecification
Nếu bạn nghi ngờ rò rỉ bộ nhớ, bạn có thể ghi log dữ liệu này mỗi giờ và tìm ra xem có số lượng đối tượng nào liên tục tăng mà không bao giờ giảm không.
Vui vẻ với đối tượng
Khi dùng ObjectSpace, bạn có quyền truy cập vào chính các đối tượng, không chỉ thông tin về chúng, nên bạn có thể làm những điều thú vị như in giá trị của tất cả chuỗi hoặc in đường dẫn của tất cả đối tượng File.
Ví dụ:
ObjectSpace
.each_object(String)
.sort_by { |s| s.size }
.each { |s| p s }
Điều này sẽ in tất cả chuỗi trong bộ nhớ, sắp xếp theo kích thước. Bạn sẽ nhận thấy có nhiều chuỗi bạn không tự tạo, chúng được tạo bởi trình thông dịch Ruby.
Ứng dụng thực tế?
Thực ra, điều này chủ yếu dùng cho gỡ lỗi và thu thập thống kê về ứng dụng 🙂
Kích thước bộ nhớ đối tượng
Một điều khác bạn có thể làm là dùng ObjectSpace.memsize_of để tìm kích thước bộ nhớ của một đối tượng cụ thể.
Ví dụ:
o = "a" * 100 ObjectSpace.memsize_of(o)
Một điều cần nhớ là cảnh báo từ tài liệu:
"Lưu ý rằng kích thước trả về là không hoàn chỉnh. Bạn cần đối phó với thông tin này chỉ như một GỢI Ý."
Nếu bạn thử phương thức này với các loại đối tượng khác nhau, bạn sẽ tìm thấy những điều thú vị, như Fixnum luôn trả về 0.
ObjectSpace.memsize_of(42) # 0
Lý do là Ruby không tạo nội bộ đối tượng Fixnum, bạn có thể học thêm về điều này trong bài viết tôi viết về số trong Ruby.
Một cái thú vị khác là chuỗi:
ObjectSpace.memsize_of("A" * 22) # 40
ObjectSpace.memsize_of("A" * 23) # 40
ObjectSpace.memsize_of("A" * 24) # 65
Tôi dùng
"A" * sizenhư cách tạo chuỗi dài hơn mà không phải gõ ra 🙂
Đợi đã! Điều gì vừa xảy ra?
Thực ra, Ruby có tối ưu hóa tích hợp cho chuỗi nhỏ hơn 24 ký tự, đó là lý do có nhảy vọt sử dụng bộ nhớ sau ngưỡng đó. Bạn có thể thấy chi tiết hơn trong bài viết của Pat Shaughnessy.
Cách tìm phương thức alias
Không hay nếu có một danh sách "master" của tất cả phương thức alias trong Ruby?
Ước mơ thành hiện thực!
Hãy xem điều nà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 lấy mã này từ một câu trả lời Stackoverflow. Nó định nghĩa phương thức aliased_methods trên lớp Module, dùng phương thức instance_methods để lấy danh sách tất cả phương thức instance được định nghĩa trên một lớp.
Tôi biết điều đó nghe hơi rối, nhưng đó là metaprogramming cho bạn!
Đây là phần còn lại của mã, xây dựng mảng tất cả tên lớp có ít nhất một đối tượng 'sống', sau đó gọi aliased_methods trên mỗi lớp và in đầu ra.
objects = ObjectSpace.each_object.map(&:class).uniq
objects.each do |klass|
methods = "\n#{klass}\n#{'-'*20}\n"
klass.send(:aliased_methods).each do |m1, m2|
methods << "#{m1.to_s.ljust(15)} #{m2}\n"
end
puts methods
end
Đây là giao diện đầu ra:
Array -------------------- inspect to_s [] slice length size find_index index collect map collect! map!
Kết luận
Tôi hy vọng bạn đã thích học về những điều thú vị bạn có thể làm với ObjectSpace, giờ hãy thử nó và cho tôi biết nếu bạn tìm thấy gì thú vị!
Đừng quên chia sẻ bài viết này với tất cả bạn lập trình viên, nó sẽ giúp họ học điều mới và giúp tôi có thêm độc giả 🙂