Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เข้าใจ ObjectSpace: ตรวจสอบสถานะ Runtime ของแอปพลิเคชัน Ruby

ถ้าคุณกำลังสงสัยว่าแอปพลิเคชัน Ruby ของคุณกำลังเกิดอะไรขึ้น...

ไม่มี fancy GUI tools ให้ใช้...

แต่เรามีโมดูล ObjectSpace!

ObjectSpace ให้ข้อมูลเกี่ยวกับสถานะปัจจุบันของแอปพลิเคชัน

มาค้นพบว่ามันทำงานยังไง

นับออบเจ็กต์ (Counting Objects)

ใช้ ObjectSpace คุณรู้ได้ว่ามีออบเจ็กต์ไหนบ้างที่ 'alive' อยู่ในโปรแกรม

ออบเจ็กต์ "alive" หมายถึงอะไร?

ออบเจ็กต์ alive ตราบใดที่ยังมี reference ชี้ไปยังมัน Reference คือวิธีเข้าถึงออบเจ็กต์ เช่น ตัวแปรหรือ constant

ถ้าออบเจ็กต์เข้าถึงไม่ได้ แปลว่ามันปลอดภัยที่จะถูกลบจากหน่วยความจำ

ตัวอย่าง:

# ตัวแปร 'name' ชี้ไปที่ string 'Dave'
name = 'Dave'
# ตอนนี้ 'name' ชี้ไปที่ 'John'
# 'Dave' ไม่มี reference ชี้แล้ว
name = 'John'

มา看看 ObjectSpace ในการทำงาน:

require 'objspace'
# รูปแบบนี้ valid Ruby syntax แต่ไม่ทำงานบน 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 (ใช้กับ 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}" }

จะพิมพ์ตารางจำนวนออบเจ็กต์สำหรับ top-10 คลาส

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

ถ้าคุณสงสัย memory leak ลอง log ข้อมูลนี้ทุกชั่วโมง ดูว่ามี object count ไหนเพิ่มขึ้นตลอดแต่ไม่ลดลง

สนุกกับออบเจ็กต์ (Fun with Objects)

เวลาใช้ ObjectSpace คุณเข้าถึงออบเจ็กต์จริง ไม่ใช่แค่ข้อมูลเกี่ยวกับมัน ทำให้ทำสิ่งสนุกๆ ได้ เช่น พิมพ์ค่าของสตริงทั้งหมด หรือพิมพ์พาธของ File objects ทั้งหมด

ตัวอย่าง:

ObjectSpace
  .each_object(String)
  .sort_by { |s| s.size }
  .each { |s| p s }

จะพิมพ์สตริงในหน่วยความจำทั้งหมด เรียงตามขนาด คุณจะสังเกตว่ามีสตริงเยอะที่คุณไม่ได้สร้างเอง มันถูกสร้างโดย Ruby interpreter

ประโยชน์จริง? ส่วนใหญ่สำหรับ debugging และเก็บสถิติของแอป 🙂

ขนาดหน่วยความจำของออบเจ็กต์ (Object Memory Size)

อีกอย่างคือใช้ ObjectSpace.memsize_of หาขนาดหน่วยความจำของออบเจ็กต์เฉพาะ

ตัวอย่าง:

o = "a" * 100
ObjectSpace.memsize_of(o)

สิ่งหนึ่งที่ต้องจำ: คำเตือนจากเอกสาร:

"Note that the return size is incomplete. You need to deal with this information as only a HINT."

ถ้าลองกับประเภทออบเจ็กต์ต่างๆ คุณจะพบสิ่งน่าสนใจ เช่น Fixnum คืนค่า 0 เสมอ

ObjectSpace.memsize_of(42) # 0

เหตุผลคือ Ruby ไม่สร้างออบเจ็กต์ Fixnum ภายใน คุณอ่านเพิ่มได้ในบทความที่ผมเขียนเกี่ยวกับตัวเลขใน Ruby

อีกอย่างน่าสนใจคือสตริง:

ObjectSpace.memsize_of("A" * 22) # 40
ObjectSpace.memsize_of("A" * 23) # 40
ObjectSpace.memsize_of("A" * 24) # 65

ผมใช้ "A" * size สร้างสตริงยาวโดยไม่ต้องพิมพ์มัน 🙂

รอ! อะไรเกิดขึ้น?

รู้ไหม Ruby มี optimization built-in สำหรับสตริงสั้นกว่า 24 ตัวอักษร นั่นคือเหตุผลที่ memory usage กระโดดหลังจากนั้น รายละเอียดเพิ่มเติมในโพสต์ของ Pat Shaughnessy

วิธีหา Aliased Methods

ดีไหมถ้ามี 'master list' ของ aliased methods ทั้งหมดใน Ruby?

ขอให้เป็นจริง!

ดูเลย:

class Module
  def aliased_methods
    instance_methods(false)
      .group_by { |m| instance_method(m) }
      .map(&:last)
      .keep_if { |symbols| symbols.length > 1 }
  end
end

โค้ดนี้มาจากคำตอบ Stackoverflow มันกำหนดเมธอด aliased_methods บนคลาส Module ใช้ instance_methods หา instance methods ทั้งหมดที่กำหนดบนคลาส

อาจดูสับสน แต่นั่นคือ metaprogramming!

โค้ดส่วนที่เหลือ สร้างอาร์เรย์ของชื่อคลาสที่มีออบเจ็กต์ alive อย่างน้อยหนึ่งตัว แล้วเรียก aliased_methods บนทุกคลาสและพิมพ์ผล

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

ผลลัพธ์หน้าตาประมาณนี้:

Array
--------------------
inspect to_s
[] slice
length size
find_index index
collect map
collect! map!

สรุป

หวังว่าคุณเพลิดเพลินกับสิ่งสนุกๆ ที่ทำได้กับ ObjectSpace ลองไปลองดูและบอกมาถ้าพบอะไรน่าสนใจ!

อย่าลืมแชร์บทความนี้ให้เพื่อนโปรแกรมเมอร์ทุกคน จะช่วยให้พวกเขาเรียนรู้สิ่งใหม่ และช่วยให้ผมได้ผู้อ่านมากขึ้น 🙂