ในการใช้งาน C # และ Java วัตถุมักจะมีตัวชี้เดียวกับคลาส สิ่งนี้เป็นไปได้เพราะเป็นภาษาที่สืบทอดเดี่ยว โครงสร้างคลาสนั้นมี vtable สำหรับลำดับชั้นการสืบทอดเดี่ยว แต่วิธีการโทรอินเตอร์เฟสมีปัญหาทั้งหมดของการสืบทอดหลายรายการเช่นกัน โดยทั่วไปจะถูกแก้ไขโดยการเพิ่ม vtables เพิ่มเติมสำหรับอินเตอร์เฟสที่นำไปใช้ทั้งหมดลงในโครงสร้างคลาส สิ่งนี้ช่วยประหยัดพื้นที่เมื่อเทียบกับการใช้งานการสืบทอดเสมือนโดยทั่วไปใน C ++ แต่ทำให้วิธีการอินเทอร์เฟซการส่งซับซ้อนมากขึ้น - ซึ่งสามารถชดเชยบางส่วนได้โดยการแคช
เช่นใน OpenJDK JVM แต่ละคลาสจะมีอาร์เรย์ของ vtables สำหรับอินเตอร์เฟสที่ใช้งานทั้งหมด (อินเตอร์เฟส vtable เรียกว่าitable ) เมื่อมีการเรียกวิธีการอินเทอร์เฟซอาร์เรย์นี้ถูกค้นหาเป็นเชิงเส้นเพื่อหาได้ของอินเทอร์เฟซนั้นจากนั้นวิธีสามารถถูกส่งผ่าน itable นั้น การแคชถูกใช้เพื่อให้ไซต์การโทรแต่ละครั้งจดจำผลลัพธ์ของวิธีการแจกจ่ายดังนั้นการค้นหานี้จะต้องทำซ้ำเมื่อการเปลี่ยนแปลงประเภทวัตถุที่เป็นรูปธรรมเท่านั้น Pseudocode สำหรับวิธีการจัดส่ง:
// Dispatch SomeInterface.method
Method const* resolve_method(
Object const* instance, Klass const* interface, uint itable_slot) {
Klass const* klass = instance->klass;
for (Itable const* itable : klass->itables()) {
if (itable->klass() == interface)
return itable[itable_slot];
}
throw ...; // class does not implement required interface
}
(เปรียบเทียบรหัสจริงในตัวแปล OpenJDK HotSpot หรือคอมไพเลอร์ x86 )
C # (หรือแม่นยำกว่า CLR) ใช้วิธีการที่เกี่ยวข้อง อย่างไรก็ตามที่นี่ itables ไม่ได้มีตัวชี้ไปยังวิธีการ แต่เป็นแผนที่แมป: พวกเขาชี้ไปที่รายการใน vtable หลักของชั้นเรียน เช่นเดียวกับ Java การค้นหา itable ที่ถูกต้องเป็นเพียงสถานการณ์ที่เลวร้ายที่สุดและคาดว่าการแคชที่ไซต์การโทรสามารถหลีกเลี่ยงการค้นหานี้ได้เกือบตลอดเวลา CLR ใช้เทคนิคที่เรียกว่า Virtual Stub Dispatch เพื่อแก้ไขรหัสเครื่องที่คอมไพล์ด้วย JIT ด้วยกลยุทธ์การแคชที่แตกต่างกัน pseudocode:
Method const* resolve_method(
Object const* instance, Klass const* interface, uint interface_slot) {
Klass const* klass = instance->klass;
// Walk all base classes to find slot map
for (Klass const* base = klass; base != nullptr; base = base->base()) {
// I think the CLR actually uses hash tables instead of a linear search
for (SlotMap const* slot_map : base->slot_maps()) {
if (slot_map->klass() == interface) {
uint vtable_slot = slot_map[interface_slot];
return klass->vtable[vtable_slot];
}
}
}
throw ...; // class does not implement required interface
}
ข้อแตกต่างที่สำคัญของ OpenJDK-pseudocode คือใน OpenJDK แต่ละคลาสมีอาเรย์ของอินเตอร์เฟสที่นำไปใช้งานทั้งทางตรงและทางอ้อมในขณะที่ CLR เก็บเฉพาะอาเรย์ของแผนที่สล็อตสำหรับอินเตอร์เฟสที่นำมาใช้โดยตรงในคลาสนั้น เราต้องเดินตามลำดับชั้นการสืบทอดขึ้นไปจนกว่าจะพบสล็อตแมพ สำหรับลำดับชั้นการสืบทอดที่ลึกส่งผลให้ประหยัดพื้นที่ สิ่งเหล่านี้มีความเกี่ยวข้องเป็นพิเศษใน CLR เนื่องจากวิธีการใช้งาน generics: สำหรับความเชี่ยวชาญทั่วไปโครงสร้างคลาสจะถูกคัดลอกและวิธีการใน vtable หลักอาจถูกแทนที่ด้วยความเชี่ยวชาญ แมปสล็อตจะยังคงชี้ไปที่รายการ vtable ที่ถูกต้องและสามารถแชร์ระหว่างความเชี่ยวชาญทั่วไปทั้งหมดของคลาสได้
ในฐานะที่เป็นบันทึกสิ้นสุดมีความเป็นไปได้มากขึ้นในการใช้การจัดส่งส่วนต่อประสาน แทนที่จะวางตัวชี้ vtable / itable ไว้ในวัตถุหรือในโครงสร้างของคลาสเราสามารถใช้พอยน์เตอร์พอยต์ไปยังวัตถุที่เป็น(Object*, VTable*)
คู่ ข้อเสียเปรียบคือสิ่งนี้จะเพิ่มขนาดของพอยน์เตอร์เป็นสองเท่าและอัพคาสต์ (จากประเภทคอนกรีตไปเป็นประเภทอินเตอร์เฟส) นั้นไม่ฟรี แต่มันมีความยืดหยุ่นมากกว่ามีทิศทางที่น้อยกว่าและยังหมายความว่าอินเตอร์เฟสสามารถนำไปใช้ภายนอกจากคลาสได้ วิธีการที่เกี่ยวข้องนั้นถูกใช้โดยส่วนต่อประสานคุณลักษณะลักษณะสนิมและประเภทของ Haskell
การอ้างอิงและการอ่านเพิ่มเติม:
- : วิกิพีเดียแคช Inline อธิบายถึงวิธีการแคชที่สามารถใช้เพื่อหลีกเลี่ยงวิธีการค้นหาที่แพง โดยทั่วไปแล้วไม่จำเป็นต้องใช้สำหรับการแจกจ่ายแบบอิง vtable แต่เป็นที่ต้องการอย่างมากสำหรับกลไกการจัดส่งที่มีราคาแพงกว่าเช่นกลยุทธ์การส่งต่ออินเตอร์เฟสด้านบน
- OpenJDK วิกิพีเดีย (2013): โทรอินเตอร์เฟซ กล่าวถึง itables
- Pobar, Neward (2009): SSCLI 2.0 Internals บทที่ 5 ของหนังสือเล่มนี้กล่าวถึงแผนที่สล็อตโดยละเอียด ก็ไม่เคยตีพิมพ์ แต่ทำใช้ได้โดยผู้เขียนในบล็อกของพวกเขา เชื่อมโยงรูปแบบไฟล์ PDFได้ย้ายตั้งแต่ หนังสือเล่มนี้อาจไม่สะท้อนสถานะปัจจุบันของ CLR อีกต่อไป
- CoreCLR (2006): เสมือนกุดส่ง ใน: หนังสือของรันไทม์ พูดถึงแผนที่สล็อตและแคชเพื่อหลีกเลี่ยงการค้นหาราคาแพง
- เคนเนดีไซย์ (2001): การออกแบบและการดำเนินงานของ Generics สำหรับ .NET ทั่วไปรันไทม์ภาษา ( ลิงก์ PDF ) กล่าวถึงวิธีการต่าง ๆ ในการใช้งานข้อมูลทั่วไป Generics โต้ตอบกับวิธีการจัดส่งเพราะวิธีการอาจมีความเชี่ยวชาญดังนั้น vtables อาจจะต้องมีการเขียนใหม่