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

Ruby Internals:สำรวจเค้าโครงหน่วยความจำของ Ruby Objects

คุณต้องการทัวร์ชม Ruby internals อย่างรวดเร็วหรือไม่

ถ้าอย่างนั้นคุณก็พร้อมรับของสมนาคุณ

เพราะ

เราจะมาร่วมกันสำรวจว่าวัตถุ Ruby ถูกจัดวางในหน่วยความจำอย่างไร และคุณจะจัดการโครงสร้างข้อมูลภายในเพื่อทำสิ่งเจ๋งๆ ได้อย่างไร

คาดเข็มขัดนิรภัยและเตรียมพร้อมสำหรับการเดินทางสู่ส่วนลึกของล่าม Ruby!

เค้าโครงหน่วยความจำของอาร์เรย์

เมื่อคุณสร้างอาร์เรย์ Ruby จะต้องสำรองข้อมูลนั้นด้วยหน่วยความจำระบบบางส่วนและข้อมูลเมตาเล็กน้อย

ข้อมูลเมตารวมถึง :

  • ขนาดอาร์เรย์ (จำนวนรายการ)
  • ความจุอาร์เรย์
  • คลาส
  • สถานะวัตถุ (ค้างหรือไม่)
  • ตัวชี้ตำแหน่งที่ข้อมูลถูกเก็บไว้ในหน่วยความจำ

เนื่องจากตัวแปล Ruby หลัก (MRI) เขียนด้วยภาษา C จึงไม่มีวัตถุ

แต่มีอย่างอื่น:โครงสร้าง .

struct ใน C ช่วยให้คุณเก็บข้อมูลที่เกี่ยวข้องไว้ด้วยกัน และสิ่งนี้ถูกใช้อย่างมากในซอร์สโค้ดของ MRI เพื่อแสดงสิ่งต่างๆ เช่น Array , String ของ &วัตถุชนิดอื่นๆ

เมื่อดูโครงสร้างเหล่านี้ เราสามารถสรุปเค้าโครงหน่วยความจำของวัตถุได้

มาดูโครงสร้างสำหรับ Array เรียกว่า RArray :

struct RArray {
  struct RBasic basic;

  union {
    struct {
      long len;

      union {
        long capa;
        VALUE shared;
      } aux;

      const VALUE *ptr;
    } heap;

    const VALUE ary[RARRAY_EMBED_LEN_MAX];
  } as;
};

ฉันรู้ว่านี่อาจดูน่ากลัวเล็กน้อยถ้าคุณไม่คุ้นเคยกับ C แต่อย่ากังวล! ฉันจะช่วยคุณแบ่งสิ่งนี้ออกเป็นชิ้นย่อยที่ย่อยง่าย 🙂

อย่างแรกที่เรามีคือ RBasic สิ่งซึ่งเป็นโครงสร้าง:

struct RBasic {
  VALUE flags;
  VALUE klass;
}

นี่คือสิ่งที่อ็อบเจกต์ Ruby ส่วนใหญ่มี &มันมีบางสิ่งเช่นคลาสสำหรับอ็อบเจกต์นี้ &แฟล็กไบนารีบางตัวที่บอกว่าอ็อบเจกต์นี้ถูกตรึงหรือไม่ (และสิ่งอื่น ๆ เช่นแอตทริบิวต์ 'tainted')

กล่าวอีกนัยหนึ่ง :

RBasic มีข้อมูลเมตาทั่วไปสำหรับวัตถุ

หลังจากนั้นเรามีโครงสร้างอื่นซึ่งมีความยาวของอาร์เรย์ (len )

นิพจน์สหภาพบอกว่า aux สามารถเป็นได้ทั้ง capa (สำหรับความจุ) หรือ shared . ส่วนใหญ่เป็นการเพิ่มประสิทธิภาพ ซึ่งมีการอธิบายรายละเอียดเพิ่มเติมในโพสต์ที่ยอดเยี่ยมนี้โดย Pat Shaughnessy ในแง่ของการจัดสรรหน่วยความจำ คอมไพเลอร์จะใช้ประเภทที่ใหญ่ที่สุดภายในสหภาพ

จากนั้นเราก็มี ptr ซึ่งมีที่อยู่หน่วยความจำที่ Array . จริง ข้อมูลจะถูกเก็บไว้

นี่คือรูปภาพของสิ่งที่ดูเหมือน (กล่องสีขาว/เทาทุกกล่องมีขนาด 4 ไบต์ในระบบ 32 บิต ):

Ruby Internals:สำรวจเค้าโครงหน่วยความจำของ Ruby Objects

คุณสามารถดูขนาดหน่วยความจำของวัตถุโดยใช้โมดูล ObjectSpace:

require 'objspace'

ObjectSpace.memsize_of([])
# 20

ตอนนี้เราพร้อมที่จะสนุกแล้ว!

Fiddle:การทดลองแสนสนุก

RBasic มีขนาด 8 ไบต์ในระบบ 32 บิตและ 16 ไบต์ในระบบ 64 บิต เมื่อทราบสิ่งนี้แล้ว เราสามารถใช้โมดูล Fiddle เพื่อเข้าถึงไบต์หน่วยความจำดิบสำหรับวัตถุและเปลี่ยนเพื่อการทดลองที่สนุกสนาน

ตัวอย่างเช่น :

เราเปลี่ยนสถานะแช่แข็งได้ด้วยการสลับบิต

นี่คือสาระสำคัญของวิธีการตรึง แต่สังเกตว่าไม่มีวิธีการยกเลิกการตรึง

นำไปใช้เพื่อความสนุกกันเถอะ!

ขั้นแรก ให้ต้องใช้ Fiddle โมดูล (ส่วนหนึ่งของ Ruby Standard Library) และสร้างสตริงที่ตรึงไว้

require 'fiddle'

str = 'water'.freeze
str.frozen?
# true

ถัดไป:

เราต้องการที่อยู่หน่วยความจำสำหรับสตริงของเรา ซึ่งสามารถหาได้แบบนี้

memory_address = str.object_id * 2

สุดท้าย:

เราพลิกบิตที่แน่นอนที่ Ruby ตรวจสอบเพื่อดูว่าวัตถุถูกแช่แข็งหรือไม่ เรายังตรวจสอบด้วยว่าวิธีนี้ใช้การได้โดยการเรียก frozen? วิธีการ

Fiddle::Pointer.new(memory_address)[1] ^= 8

str.frozen?
# false

สังเกตว่าดัชนี [1] หมายถึงไบต์ที่ 2 ของ แฟล็ก ค่า (ซึ่งประกอบด้วยทั้งหมด 4 ไบต์)

จากนั้นเราก็ใช้ ^= ซึ่งเป็นตัวดำเนินการ “XOR” (Exclusive OR) เพื่อพลิกบิตนั้น

เราทำสิ่งนี้เพราะส่วนต่าง ๆ ภายใน แฟล็ก มีความหมายต่างกันและเราไม่ต้องการเปลี่ยนแปลงสิ่งที่ไม่เกี่ยวข้อง

หากคุณได้อ่านโพสต์ ruby ​​tricks ของฉัน คุณอาจเคยเห็นสิ่งนี้มาก่อน แต่ตอนนี้คุณรู้แล้วว่ามันทำงานอย่างไร 🙂

อีกสิ่งหนึ่งที่คุณสามารถลองได้คือเปลี่ยนความยาวของอาร์เรย์ &พิมพ์อาร์เรย์

คุณจะเห็นว่าอาร์เรย์สั้นลง!

คุณยังสามารถเปลี่ยนคลาสเพื่อสร้าง Array คิดว่าเป็น String

บทสรุป

คุณได้เรียนรู้เล็กน้อยเกี่ยวกับวิธีการทำงานของ Ruby ภายใต้ประทุน หน่วยความจำสำหรับวัตถุ Ruby ถูกจัดวางอย่างไร &คุณสามารถใช้ Fiddle . ได้อย่างไร โมดูลที่จะเล่นกับสิ่งนั้น

คุณไม่ควรใช้ Fiddle แบบนี้ในแอปจริง แต่ทดลองเล่นก็สนุก

อย่าลืม แชร์โพสต์นี้ ให้คนเห็นมากขึ้น 🙂