Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Hiểu về ObjectSpace: Kiểm tra trạng thái runtime trong ứng dụng Ruby

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" * size như 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ả 🙂