jvmmemorymodel
บทความนี้ส่วนใหญ่แนะนำพื้นที่ข้อมูลรันไทม์ (Runtimedataareas) ที่อธิบายไว้ในข้อกำหนด JVM พื้นที่เหล่านี้ได้รับการออกแบบมาเพื่อจัดเก็บข้อมูลที่ใช้โดย JVM เองหรือโปรแกรมที่ทำงานบน JVM
มาดูภาพรวมของ JVM ก่อนจากนั้นแนะนำ bytecode และในที่สุดก็แนะนำพื้นที่ข้อมูลที่แตกต่างกัน
ภาพรวม
ในฐานะที่เป็นนามธรรมของระบบปฏิบัติการ JVM ทำให้มั่นใจได้ว่ารหัสเดียวกันจะทำงานได้อย่างสม่ำเสมอในฮาร์ดแวร์หรือระบบปฏิบัติการที่แตกต่างกัน
ตัวอย่างเช่น:
สำหรับประเภทพื้นฐาน INT จะเป็นจำนวนเต็มที่ลงนาม 32 บิตโดยไม่คำนึงถึงระบบปฏิบัติการ 16 บิต/32 บิต/64 บิต อยู่ในช่วงตั้งแต่ -2^31 ถึง 2^31-1
ไม่ว่าระบบปฏิบัติการหรือฮาร์ดแวร์จะมีขนาดใหญ่หรือขนาดเล็กหรือไม่ก็มั่นใจได้ว่าข้อมูลในหน่วยความจำที่จัดเก็บและใช้โดย JVM นั้นมีขนาดใหญ่หรือเล็กขนาดใหญ่ (อ่านไบต์บิตสูงก่อน)
การใช้งาน JVM ที่แตกต่างกันอาจแตกต่างกันบ้าง แต่โดยทั่วไปจะเหมือนกัน
ภาพด้านบนเป็นภาพรวมของ JVM
JVM ตีความไบต์ที่สร้างจากคอมไพเลอร์ แม้ว่า JVM จะเป็นตัวย่อของเครื่องเสมือน Java ตราบใดที่มันเป็นภาษาที่สามารถรวบรวมลงใน bytecode ก็สามารถทำงานได้ตาม JVM เช่น Scala, Groovy <� "/kf/ware/vc/" target = "_ blank"> vcd4ncjxwps6qwcux3mpixrw3sbxetmxfzekvt6os19a92slru+gxu2nsyxnzbg9hzgvyvnpu2lkiu7q05r W91MVQ0MQXYV2+3CF41TC1XNK7UPBH+NPYO6ZWQRXAVNPU2MV8TCRJBGFZC2XVYWRLCRG7Z/Q72BVY1D9KVK3NO9A51MVQ0KGJGJPC 9WPG0KPHA+VNPU2LXE19A92SLRZAI5/DA00NDS/CFMKGV4ZWN1DGLVBIBLBMDPBMUPVFJQ0L3IYS26ZDA00NA8L3A+DQO8CD7WT ndq0v3h5tdo0qq05rsis8zq8snpz8loxkosscji57pm0plwtndqtb3exnk70ncjrlvy1d/k/b7dvmbl47xe1tc85l3hufs8l3a QO8CD7WTNDQ0V3H5TKYULRU8LSMWO3T67XXSUOY2DF3Z7XNS7XEVBU7PTWVCD4NCJXWPIOQUTY24EPWTBA8YRXP1SHLVLTKSBHGHG 0UU5PSTCKEPJVD1QDXN0IGLUIHRPBWUPOANKSVS+ZCRHSNG+RBOJ1RTQ0LXETPRC6YJIYLXJTPRC6YMX4NLRS8MXVRXYTPRC6YHO yxrpdmugq29kzsmho7tmt8vksvsx4nlryfqzybt6wuu1xmf40/kzxs6qpc9wpg0kpha+tprc67u6tobh+chdb2rlienh Y2GPOAO8TMQXSEDS67Y8YVUOSKLUKBY2TPO1XMZHUN/BY0PWTBXE0NTE3COQPC9WPG0KPGGYIGLKPQ == " สถาปัตยกรรม "> สถาปัตยกรรมที่ใช้สแต็ก
JVM ใช้สถาปัตยกรรมแบบสแต็ก แม้ว่าสแต็กจะโปร่งใสสำหรับนักพัฒนา แต่ก็มีบทบาทหรืออิทธิพลที่สำคัญมากต่อไบต์ที่สร้างขึ้นและ JVM
โปรแกรมที่เราพัฒนาจะแปลงการดำเนินงานระดับต่ำและจัดเก็บไว้ในไบต์ แผนที่ไปยังคำแนะนำการดำเนินการผ่านตัวถูกดำเนินการใน JVM ตามข้อกำหนดของ JVM พารามิเตอร์ที่ต้องการโดยคำแนะนำการดำเนินการจะได้รับจากสแต็กตัวถูกดำเนินการ
มายกตัวอย่างการเพิ่มสองหมายเลข การดำเนินการนี้เรียกว่า IADD ต่อไปนี้เป็นกระบวนการของ 3+4 ใน bytecode
กด 3 และ 4 ครั้งแรกลงในสแต็คตัวถูกดำเนินการ
โทรหาคำสั่ง IADD
คำสั่ง IADD จะปรากฏขึ้น 2 หมายเลขจากด้านบนของตัวถูกดำเนินการสแต็ก
ผลลัพธ์ของ 3+4 ถูกผลักเข้าไปในสแต็กตัวถูกดำเนินการเพื่อใช้ในภายหลัง
วิธีการนี้เรียกว่าสถาปัตยกรรมที่ใช้สแต็ก มีวิธีอื่น ๆ ในการจัดการการดำเนินงานระดับต่ำเช่นสถาปัตยกรรมตามการลงทะเบียน
bytecode
Java bytecode เป็นผลมาจากการแปลงซอร์สโค้ด Java เป็นชุดของการดำเนินการระดับต่ำ การดำเนินการแต่ละครั้งประกอบด้วย opcode หรือรหัสการทำงานที่มีพารามิเตอร์ความยาวไบต์หรือมากกว่า (แต่การใช้งานส่วนใหญ่ใช้พารามิเตอร์ที่ได้รับผ่านสแต็กตัวถูกดำเนินการ) หนึ่งไบต์สามารถแสดงหมายเลข 256 จาก 0x00 ถึง 0xff และปัจจุบันไปยัง Java8 มีการใช้ทั้งหมด 204
แสดงรายการ bytecode opcodes ประเภทต่าง ๆ รวมถึงช่วงและคำอธิบายที่เรียบง่ายของพวกเขา
ค่าคงที่: ผลักดันค่าของพูลคงที่หรือค่าที่รู้จักในสแต็กตัวถูกดำเนินการ 0x00 - 0x14
โหลด: กดค่าตัวแปรในพื้นที่ลงในสแต็กตัวถูกดำเนินการ 0x15 - 0x35
ร้านค้า: การโหลดค่าจากตัวถูกดำเนินการไปยังตัวแปรท้องถิ่น 0x36 - 0x56
สแต็ค: กระบวนการดำเนินการสแต็ค 0x57 - 0x5f
คณิตศาสตร์: รับค่าจาก Operand Stack สำหรับการคำนวณทางคณิตศาสตร์ขั้นพื้นฐาน 0x60 - 0x84
การแปลง: แปลงระหว่างประเภท 0x85 - 0x 93
Comaprisons: การเปรียบเทียบการดำเนินการสองค่า 0x94 - 0xa6
การควบคุม: ดำเนินการ goto, return, loop ฯลฯ การดำเนินการควบคุม 0xa7 - 0xb1
การอ้างอิง: ดำเนินการจัดสรรวัตถุหรืออาร์เรย์และรับหรือตรวจสอบการอ้างอิงไปยังวัตถุวิธีการและวิธีการคงที่ วิธีการคงที่สามารถเรียกได้ 0xb2 - oxc3
Extended: Extended: การดำเนินการจากหมวดหมู่อื่น ๆ ที่เพิ่มเข้ามาหลังจาก จากค่า 0xc4 ถึง 0xc9
(ประโยคนี้หมายถึงอะไร?
สงวนไว้: การใช้งาน JVM ใช้สล็อต 0xca, oxfe, oxff
การดำเนินการ 204 ครั้งนี้ง่ายมากยกตัวอย่าง
IFEQ (0x99) กำหนดว่าทั้งสองค่าเท่ากัน
IADD (0x60) เพิ่มสองตัวเลข
I2L (0x85) แปลงความยาวเล็กน้อย
arraylength (0xbe) ส่งคืนความยาวอาร์เรย์
ป๊อป (0x57) ป๊อปค่าจากด้านบนของตัวถูกดำเนินการสแต็ก
เราต้องการคอมไพเลอร์เพื่อสร้างไฟล์ bytecode และคอมไพเลอร์ Java มาตรฐานคือ Javac ใน JDK
การทดสอบระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {int a = 1; int b = 15; int result = เพิ่ม (a, b); } public static int add (int a, int b) {int result = a + b; ผลการกลับมา; -คุณสามารถรับไฟล์ bytecode ของ "test.class" ผ่าน "javac test.java" ไฟล์ bytecode เป็นไบนารีเราสามารถแปลงไฟล์ไบนารีไบนารีเป็นรูปแบบข้อความผ่าน javap
การทดสอบ java -verbose. class
classfile /c:/tmp/test.class แก้ไขล่าสุด 1 avr 2558; ขนาด 367 ไบต์ MD5 ตรวจสอบ ADB9FF75F12FC6CE1CDDE22A9C4C7426 รวบรวมจาก "test.java" คลาสสาธารณะ com.codinggeek.jvm.test SourceFile: "test.java" รุ่นรอง: 0 เมเจอร์เวอร์ชัน: 51 Flags java/lang/object. "<init>" :() v #2 = methodRef #3. #16 // com/codinggeek/jvm/test.add: (ii) ฉัน #3 = คลาส #17 // com/codinggeek/jvm/test #4 = คลาส #18 // java/lang/object #5 = utf8 LinenumberTable #9 = UTF8 Main #10 = UTF8 ([ljava/lang/string;) v #11 = utf8 เพิ่ม #12 = utf8 (ii) i #13 = utf8 sourcefile #14 = utf8 test.java #15 = nameandtype #5: #6 // #17 = utf8 com/codinggeek/jvm/test #18 = utf8 java/lang/object {public com.codinggeek.jvm.test (); Flags: ACC_Public Code: stack = 1, locals = 1, args_size = 1 0: aload_0 1: invokespecial #1 // วิธี java/lang/object. "<init> "() v 4: ส่งคืนผ้าลินิน Flags: acc_public, acc_static code: stack = 2, locals = 4, args_size = 1 0: iconst_1 1: istore_1 2: bipush 15 4: istore_2 5: iload_1 6: iload_2 7: invokestatic #2 // วิธีเพิ่ม: (ii) i บรรทัดที่ 9: 11 INT แบบคงที่สาธารณะ (int, int); Flags: acc_public, acc_static code: stack = 2, locals = 3, args_size = 2 0: iload_0 1: iload_1 2: iadd 3: istore_2 4: iload_2 5: ireturn linenumbertable: บรรทัดที่ 12: 0 บรรทัด 13: 4}จะเห็นได้ว่า bytecode ไม่ได้เป็นเพียงการแปลง่าย ๆ ของรหัส Java แต่รวมถึง:
คำอธิบายพูลคงที่ของชั้นเรียน พูลคงที่เป็นพื้นที่ข้อมูล JVM ที่ใช้ในการจัดเก็บข้อมูลเมตาคลาสเช่นชื่อวิธีรายการพารามิเตอร์ ฯลฯ ภายในชั้นเรียน เมื่อ JVM โหลดคลาสข้อมูลเมตาจะถูกโหลดลงในพูลคงที่
ให้ข้อมูลตำแหน่งเฉพาะของฟังก์ชั่นและตัวแปร TMALL ใน bytecode ผ่านตารางหมายเลขแถวและตารางตัวแปรท้องถิ่น
การแปลรหัส Java (รวมถึงการสร้างคลาสแม่ที่ซ่อนอยู่)
ให้การดำเนินการที่เฉพาะเจาะจงมากขึ้นบนตัวถูกดำเนินการและวิธีที่สมบูรณ์มากขึ้นในการส่งผ่านและรับพารามิเตอร์
ด้านล่างนี้เป็นคำอธิบายง่ายๆของข้อมูลการจัดเก็บไฟล์ bytecode
ClassFile {U4 Magic; U2 minor_version; U2 major_version; U2 constant_pool_count; cp_info constant_pool [constant_pool_count-1]; u2 access_flags; U2 this_class; U2 super_class; U2 interfaces_count; อินเตอร์เฟส U2 [interfaces_count]; U2 fields_count; ฟิลด์ Field_info [fields_count]; u2 attributes_count; attribute_info แอตทริบิวต์ [attributes_count];}พื้นที่ข้อมูลรันไทม์
พื้นที่ข้อมูลรันไทม์เป็นพื้นที่หน่วยความจำที่ออกแบบมาเพื่อจัดเก็บข้อมูล ข้อมูลนี้ใช้สำหรับนักพัฒนาหรือ JVM ภายใน
กอง
ฮีปถูกสร้างขึ้นเมื่อ JVM เริ่มต้นและถูกแชร์โดยกระทู้ JVM ทั้งหมด อินสแตนซ์และอาร์เรย์ของคลาสทั้งหมดจะถูกจัดสรรให้กับกอง (สร้างโดยใหม่)
กองจะต้องได้รับการจัดการโดยนักสะสมขยะซึ่งรับผิดชอบในการปล่อยวัตถุที่สร้างโดยนักพัฒนาและจะไม่ถูกใช้อีก
สำหรับกลยุทธ์การรวบรวมขยะจะถูกกำหนดโดยการใช้งาน JVM (ตัวอย่างเช่นฮอตสปอตให้อัลกอริทึมหลายรายการ)
มีขีด จำกัด สูงสุดสำหรับหน่วยความจำกอง หากเกินค่านี้ JVM จะทำการยกเว้น OutofMemroy
พื้นที่วิธีการ
พื้นที่วิธีการนี้ยังใช้ร่วมกันโดยกระทู้ทั้งหมดของ JVM สิ่งเดียวกันนี้ถูกสร้างขึ้นด้วยการเริ่มต้น JVM ข้อมูลที่เก็บไว้ในพื้นที่เมธอดถูกโหลดจาก bytecode โดย classloader ซึ่งจะมีอยู่อย่างต่อเนื่องในระหว่างการทำงานของแอปพลิเคชันเว้นแต่ว่า classloader โหลดพวกเขาจะถูกทำลายหรือหยุด JVM
พื้นที่วิธีเก็บข้อมูลต่อไปนี้:
ข้อมูลคลาส (ชื่อแอตทริบิวต์ชื่อวิธีชื่อคลาสพาเรนต์ชื่อข้อแก้ตัวเวอร์ชัน ฯลฯ )
วิธีการและสร้างไบต์
Runtime Constant Pool ที่สร้างขึ้นเมื่อโหลดแต่ละคลาส
ข้อกำหนด JVM ไม่ได้บังคับพื้นที่วิธีการที่จะนำไปใช้ในกอง ก่อนที่จะใช้ Java7 ฮอตสปอตใช้โซนวิธีการโดยใช้ภูมิภาคที่เรียกว่า Permgen วงดนตรีถาวรอยู่ติดกับฮีป (การจัดการหน่วยความจำเหมือนกับฮีป) บิตเริ่มต้นคือ 64MB
เริ่มต้นจาก Java8 HPTSpot ใช้หน่วยความจำท้องถิ่นแยกต่างหากเพื่อใช้พื้นที่วิธีการและตั้งชื่อพื้นที่ข้อมูลเมตา (Metaspace) พื้นที่ว่างสูงสุดในพื้นที่ข้อมูลเมตาคือหน่วยความจำที่มีอยู่ของระบบทั้งหมด
หากวิธีการไม่สามารถใช้สำหรับหน่วยความจำที่มีอยู่ JVM จะโยน outofMemoryError
Runtime Constant Pool
พูลคงที่รันไทม์เป็นส่วนหนึ่งของพื้นที่วิธีการ เนื่องจากความสำคัญของการเรียกใช้พูลคงที่ไปยังข้อมูลเมตาจึงอธิบายแยกต่างหากในข้อกำหนด Java นอกพื้นที่วิธีการ พูลคงที่รันไทม์จะเติบโตด้วยคลาสที่โหลดและอินเทอร์เฟซ
สระว่ายน้ำคงที่เป็นเล็กน้อยของตารางไวยากรณ์ในภาษาดั้งเดิม กล่าวอีกนัยหนึ่งเมื่อมีการเรียกคลาสวิธีการหรือคุณสมบัติ JVM ค้นหาที่อยู่จริงของข้อมูลนี้ในหน่วยความจำผ่านพูลคงที่รันไทม์ พูลคงที่รันไทม์ยังมีค่าคงที่ของตัวอักษรสตริงหรือประเภทดั้งเดิม
stirng myString = "นี่คือสตริงซิตเตอร์" สุดท้าย int my_constant = 2;
พีซี (ตัวนับโปรแกรม) ลงทะเบียน (ต่อเธรด) การลงทะเบียนพีซี (ต่อเธรด)
แต่ละเธรดมีการลงทะเบียนพีซี (ตัวนับโปรแกรม) ซึ่งสร้างขึ้นพร้อมกับการสร้างเธรด แต่ละเธรดสามารถเรียกใช้วิธีเดียวได้ที่จุดหนึ่งในเวลาที่เรียกว่าวิธีการปัจจุบันของเธรด การลงทะเบียนพีซีมีที่อยู่ของ JVM กำลังดำเนินการตามคำสั่ง (ในพื้นที่วิธีการ)
หากวิธีการที่ดำเนินการในปัจจุบันเป็นวิธีการในท้องถิ่นค่าของการลงทะเบียนพีซีจะไม่ได้กำหนดไว้
สแต็กเครื่องเสมือนต่อเธรด-java-virtual-machine-stacks-per-thread "> เสมือนเครื่องสแต็ก (ต่อเธรด) Java เสมือนเครื่องเสมือน (ต่อเธรด)
สแต็กเครื่องเสมือนเก็บหลายเฟรมดังนั้นก่อนที่จะอธิบายสแต็กลองมาดูเฟรมก่อน
เฟรม
เฟรมคือโครงสร้างข้อมูลที่มีข้อมูลหลายข้อมูลที่แสดงถึงวิธีการปัจจุบันระบุว่าเธรดกำลังดำเนินการ:
ตัวถูกดำเนินการสแต็ค: ดังที่ได้กล่าวไว้ก่อนหน้านี้คำแนะนำไบต์ใช้ตัวถูกดำเนินการสแต็กเพื่อส่งพารามิเตอร์
อาร์เรย์ตัวแปรท้องถิ่น: อาร์เรย์นี้มีตัวแปรท้องถิ่นทั้งหมดภายในขอบเขตของวิธีการที่ดำเนินการในปัจจุบัน อาร์เรย์นี้สามารถมีประเภทดั้งเดิมการอ้างอิงหรือที่อยู่ส่งคืน ขนาดของอาร์เรย์ตัวแปรท้องถิ่นจะถูกกำหนดในเวลาคอมไพล์ JVM ใช้ตัวแปรท้องถิ่นเพื่อผ่านพารามิเตอร์เมื่อเรียกใช้วิธีการและอาร์เรย์ตัวแปรท้องถิ่นของวิธีที่เรียกว่าถูกสร้างขึ้นผ่านสแต็กตัวถูกดำเนินการของวิธีการเรียก
การอ้างอิงพูลคงที่รันไทม์: การอ้างอิงถึงพูลคงที่ของวิธีปัจจุบันของคลาสปัจจุบัน JVM ใช้การอ้างอิงพูลคงที่เพื่อส่งสัญญาณไปยังการอ้างอิงหน่วยความจำจริง
สแต็ค (สแต็ค)
แต่ละเธรด JVM มีสแต็ก JVM ส่วนตัวซึ่งสร้างขึ้นในเวลาเดียวกันกับเธรด Java Virtual Machine Stack เก็บเฟรม ทุกครั้งที่มีการเรียกวิธีการเฟรมจะถูกสร้างและผลักเข้าไปในสแต็กเครื่องเสมือน เมื่อวิธีการนี้ถูกดำเนินการเฟรมจะถูกทำลายด้วย (ไม่ว่าวิธีการจะถูกดำเนินการตามปกติหรือมีข้อยกเว้นถูกโยนลงไป)
มีเพียงเฟรมเดียวเท่านั้นในระหว่างการดำเนินการของเธรด เฟรมนี้เรียกว่าเฟรมปัจจุบัน
การดำเนินการเกี่ยวกับตัวแปรท้องถิ่นและสแต็คตัวถูกดำเนินการมักจะมาพร้อมกับการอ้างอิงถึงเฟรมปัจจุบัน
ลองดูอีกตัวอย่างหนึ่งของการเพิ่ม
Public Int Add (int a, int b) {return a + b;} ฟังก์ชันโมฆะสาธารณะ () {// รหัสบางส่วนโดยไม่มีฟังก์ชั่นการเรียก int result = เพิ่ม (2,3); // เรียกใช้ฟังก์ชัน b // บางรหัสที่ไม่มีการเรียกใช้ฟังก์ชัน}ภายในวิธี A, Frame A คือเฟรมปัจจุบันซึ่งอยู่ที่ด้านบนของสแต็กเครื่องเสมือน ที่จุดเริ่มต้นของการเรียกวิธีการเพิ่มเฟรม B ใหม่จะถูกสร้างและผลักเข้าไปในสแต็กเครื่องเสมือน เฟรม B กลายเป็นเฟรมปัจจุบันใหม่
อาร์เรย์ตัวแปรท้องถิ่นของเฟรม B นั้นเต็มไปด้วยข้อมูลในสแต็กตัวถูกดำเนินการของเฟรม A เมื่อวิธีการเพิ่มสิ้นสุดเฟรม B ถูกทำลายและเฟรม A จะถูกสร้างขึ้นใหม่เป็นเฟรมปัจจุบัน ผลลัพธ์ของวิธีการเพิ่มจะถูกผลักเข้าไปในสแต็กตัวถูกดำเนินการของเฟรม A เพื่อให้วิธี A สามารถรับผลลัพธ์ของการเพิ่มผ่านสแต็กตัวถูกดำเนินการของเฟรม A.
สรุป
ข้างต้นคือทั้งหมดที่เกี่ยวกับการวิเคราะห์พื้นที่ข้อมูลของ Java Virtual Machine Runtime ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน
หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น