जेवीएम इंटर्नल्स, भाग 2 - कक्षा फ़ाइल संरचना

सभी को नमस्कार! लेख का अनुवाद विशेष रूप से जावा डेवलपर पाठ्यक्रम के छात्रों के लिए तैयार किया गया था।




हम इस बारे में बात करना जारी रखते हैं कि जावा वर्चुअल मशीन आंतरिक रूप से कैसे काम करती है। पिछले लेख ( अंग्रेजी में मूल ) में, हमने क्लास लोडिंग सबसिस्टम की जांच की। इस लेख में हम क्लास फाइलों की संरचना के बारे में बात करेंगे।

जैसा कि हम पहले से ही जानते हैं, जावा प्रोग्रामिंग लैंग्वेज में लिखे गए सभी सोर्स कोड को सबसे पहले javac कंपाइलर का उपयोग करके javac में संकलित किया गया है , जो जावा डेवलपमेंट किट का हिस्सा है। बाइटकोड को एक विशेष वर्ग फ़ाइल में एक बाइनरी फ़ाइल में संग्रहीत किया जाता है। फिर इन क्लास-फाइल्स को डायनामिक रूप से (यदि आवश्यक हो) क्लास लोडर (क्लासॉल्डर) द्वारा मेमोरी में लोड किया जाता है।


चित्र - जावा स्रोत कोड संकलन

.java प्रत्येक फ़ाइल को कम से कम एक .class फ़ाइल में संकलित किया जाता है। स्रोत कोड में परिभाषित प्रत्येक वर्ग, इंटरफ़ेस और मॉड्यूल के लिए, एक .class फ़ाइल बनाई गई है। यह इंटरफेस और नेस्टेड कक्षाओं पर भी लागू होता है।

नोट - सरलता के लिए, .class एक्सटेंशन वाली फ़ाइलों को "क्लास फाइल्स" कहा जाएगा।

चलो एक सरल कार्यक्रम लिखते हैं।

 public class ClassOne{ public static void main(String[] args){ System.out.println("Hello world"); } static class StaticNestedClass{ } } class ClassTwo{ } interface InterfaceOne{ } 

इस फ़ाइल के लिए javac चलाने से निम्न फ़ाइलों का परिणाम होगा।

 ClassOne$StaticNestedClass.class ClassOne.class ClassTwo.class InterfaceOne.class 

जैसा कि आप देख सकते हैं, प्रत्येक वर्ग और इंटरफ़ेस के लिए एक अलग वर्ग फ़ाइल बनाई गई है।

क्लास फ़ाइल के अंदर क्या है?


क्लास फाइल बाइनरी फॉर्मेट में है। इसमें जानकारी आम तौर पर सूचना के लगातार टुकड़ों के बीच इंडेंटेशन के बिना लिखी जाती है, सब कुछ बाइट सीमाओं के साथ गठबंधन किया जाता है। सभी 16-बिट और 32-बिट मान दो या चार लगातार 8-बिट बाइट्स का उपयोग करके लिखे गए हैं।

वर्ग फ़ाइल में निम्न जानकारी है।

मैजिक नंबर, हस्ताक्षर । प्रत्येक वर्ग फ़ाइल के पहले चार बाइट्स हमेशा 0xCAFEBABE होते हैं। ये चार बाइट जावा वर्ग फ़ाइल की पहचान करते हैं।

फ़ाइल संस्करण। अगले चार बाइट्स में फ़ाइल के प्रमुख और मामूली संस्करण होते हैं। एक साथ, ये संख्याएं क्लास फ़ाइल प्रारूप के संस्करण को निर्धारित करती हैं। यदि क्लास फ़ाइल में M का एक प्रमुख प्रमुख संस्करण है और एक मामूली m है, तो हम इस संस्करण को Mm के रूप में नामित करते हैं

प्रत्येक JVM में वर्ग फ़ाइलों के समर्थित संस्करणों की सीमाएँ हैं। उदाहरण के लिए, जावा 11 45 से 55, जावा 12 - 45 से 56 तक प्रमुख संस्करणों का समर्थन करता है।

स्थिरांक का पूल। स्ट्रिंग स्थिरांक, वर्ग नाम, इंटरफेस, फ़ील्ड, विधियाँ और अन्य स्थिरांक का प्रतिनिधित्व करने वाली संरचनाओं की एक तालिका जो क्लासफाइल संरचना और उसके उप-निर्माण में हैं। प्रत्येक निरंतर पूल तत्व एकल-बाइट टैग से शुरू होता है जो निरंतर के प्रकार को परिभाषित करता है। निरंतर के प्रकार के आधार पर, निम्न बाइट्स एक तत्काल स्थिर मान या पूल में किसी अन्य तत्व का संदर्भ हो सकता है।

पहुंच झंडे। झंडे की एक सूची जो इंगित करती है कि वर्ग या तो एक इंटरफ़ेस है, सार्वजनिक या निजी, अंतिम वर्ग या नहीं। जावा वर्चुअल मशीन विशिष्टता में विभिन्न झंडे जैसे ACC_PUBLIC , ACC_FINAL , ACC_INTERFACE , ACC_ENUM , आदि का वर्णन किया गया है।

यह वर्ग। निरंतर पूल में प्रवेश के लिए लिंक।

सुपर क्लास। निरंतर पूल में प्रवेश के लिए लिंक।

इंटरफेस। वर्ग द्वारा लागू इंटरफेस की संख्या।

खेतों की संख्या। कक्षा या इंटरफ़ेस में फ़ील्ड की संख्या।

फील्ड। खेतों की संख्या के बाद, चर लंबाई की संरचनाओं की एक तालिका निम्नानुसार है। फ़ील्ड प्रकार और नाम (स्थिरांक के पूल के संदर्भ में) के साथ प्रत्येक क्षेत्र के लिए एक।

तरीकों की संख्या। कक्षा या इंटरफ़ेस में विधियों की संख्या। इस संख्या में केवल वे विधियाँ शामिल हैं जो सुपरक्लास से विरासत में मिली विधियों के बिना, कक्षा में स्पष्ट रूप से परिभाषित हैं।

तरीके। आगे स्वयं विधियाँ हैं। प्रत्येक विधि के लिए, निम्न जानकारी समाहित है: विधि डिस्क्रिप्टर (वापसी प्रकार और तर्क सूची), विधि के स्थानीय चर के लिए आवश्यक शब्दों की संख्या, विधि ऑपरेटर स्टैक के लिए आवश्यक स्टैक शब्दों की अधिकतम संख्या, विधि द्वारा पकड़ा गया अपवाद तालिका, विधि bytecodes और तालिका लाइन नंबर।

विशेषताओं की संख्या। इस वर्ग, इंटरफ़ेस या मॉड्यूल में विशेषताओं की संख्या।

जिम्मेदार बताते हैं। विशेषताओं की संख्या तालिकाओं या चर-लंबाई संरचनाओं के बाद होती है जो प्रत्येक विशेषता का वर्णन करती हैं। उदाहरण के लिए, हमेशा एक "SourceFile" विशेषता होती है। इसमें स्रोत फ़ाइल का नाम है जिसमें से वर्ग फ़ाइल संकलित की गई थी।

हालाँकि कक्षा की फ़ाइल सीधे मानव-पठनीय नहीं है, लेकिन JDK में एक उपकरण है जिसे जावप कहा जाता है जो एक सुविधाजनक प्रारूप में अपनी सामग्री प्रदर्शित करता है।

चलो नीचे दिखाया गया है एक सरल जावा प्रोग्राम लिखें।

 package bytecode; import java.io.Serializable; public class HelloWorld implements Serializable, Cloneable { public static void main(String[] args) { System.out.println("Hello World"); } } 

आइए इस प्रोग्राम को javac साथ संकलित करें, जो HelloWorld.class फ़ाइल बनाएगा, और HelloWorld.class फ़ाइल देखने के लिए javap का उपयोग javapjavap के लिए -v (verbose) javap -v (verbose) विकल्प के साथ रनिंग javap निम्न परिणाम देता है:

 Classfile /Users/apersiankite/Documents/code_practice/java_practice/target/classes/bytecode/HelloWorld.class Last modified 02-Jul-2019; size 606 bytes MD5 checksum 6442d93b955c2e249619a1bade6d5b98 Compiled from "HelloWorld.java" public class bytecode.HelloWorld implements java.io.Serializable,java.lang.Cloneable minor version: 0 major version: 55 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #5 // bytecode/HelloWorld super_class: #6 // java/lang/Object interfaces: 2, fields: 0, methods: 2, attributes: 1 Constant pool: #1 = Methodref #6.#22 // java/lang/Object."<init>":()V #2 = Fieldref #23.#24 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #25 // Hello World #4 = Methodref #26.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #28 // bytecode/HelloWorld #6 = Class #29 // java/lang/Object #7 = Class #30 // java/io/Serializable #8 = Class #31 // java/lang/Cloneable #9 = Utf8 <init> #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Lbytecode/HelloWorld; #16 = Utf8 main #17 = Utf8 ([Ljava/lang/String;)V #18 = Utf8 args #19 = Utf8 [Ljava/lang/String; #20 = Utf8 SourceFile #21 = Utf8 HelloWorld.java #22 = NameAndType #9:#10 // "<init>":()V #23 = Class #32 // java/lang/System #24 = NameAndType #33:#34 // out:Ljava/io/PrintStream; #25 = Utf8 Hello World #26 = Class #35 // java/io/PrintStream #27 = NameAndType #36:#37 // println:(Ljava/lang/String;)V #28 = Utf8 bytecode/HelloWorld #29 = Utf8 java/lang/Object #30 = Utf8 java/io/Serializable #31 = Utf8 java/lang/Cloneable #32 = Utf8 java/lang/System #33 = Utf8 out #34 = Utf8 Ljava/io/PrintStream; #35 = Utf8 java/io/PrintStream #36 = Utf8 println #37 = Utf8 (Ljava/lang/String;)V { public bytecode.HelloWorld(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 4: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lbytecode/HelloWorld; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 7: 0 line 8: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "HelloWorld.java" 

यहां आप देख सकते हैं कि वर्ग सार्वजनिक है और निरंतर पूल में 37 प्रविष्टियां हैं। एक विशेषता (नीचे SourceFile) है, वर्ग दो इंटरफेस (सीरियल, क्लोन करने योग्य) को लागू करता है, इसमें कोई फ़ील्ड नहीं है और दो तरीके हैं।

आपने देखा होगा कि सोर्स कोड में केवल एक स्टैटिक मेन मेथड होता है, लेकिन क्लास फाइल कहती है कि दो मेथड हैं। डिफॉल्ट कंस्ट्रक्टर को याद रखें - यह javac कंपाइलर द्वारा जोड़ा गया एक नो-लॉजिक कंस्ट्रक्टर है, जिसका बाईटकोड आउटपुट में भी दिखाई देता है। कंस्ट्रक्टर्स को तरीकों के रूप में माना जाता है।

आप यहाँ जाप के बारे में अधिक पढ़ सकते हैं।

युक्ति : आप यह देखने के लिए javap का उपयोग भी कर सकते हैं कि lambdas अनाम आंतरिक वर्गों से कैसे भिन्न होता है।

Source: https://habr.com/ru/post/hi478584/


All Articles