在系統為一個程序分配的空間中,分成許多段,比如有代碼段,存放程序可執行代碼,有數據段,可以分配變數,有常量段,專門存放常量,是只讀的。常量變數可以位於數據段中,仍然是變數,只是程序沒有改動許可權。在程序執行到main之前,系統負責將常量區存儲的常量賦值給常量。
『貳』 誰能簡單闡述下java編譯執行的過程
Java虛擬機(JVM)是可運行Java代碼的假想計算機。只要根據JVM規格描述將解釋器移植到特定的計算機上,就能保證經過編譯的任何Java代碼能夠在該系統上運行。本文首先簡要介紹從Java文件的編譯到最終執行的過程,隨後對JVM規格描述作一說明。
一.Java源文件的編譯、下載、解釋和執行
Java應用程序的開發周期包括編譯、下載、解釋和執行幾個部分。Java編譯程序將Java源程序翻譯為JVM可執行代碼?位元組碼。這一編譯過程同C/C++的編譯有些不同。當C編譯器編譯生成一個對象的代碼時,該代碼是為在某一特定硬體平台運行而產生的。因此,在編譯過程中,編譯程序通過查表將所有對符號的引用轉換為特定的內存偏移量,以保證程序運行。Java編譯器卻不將對變數和方法的引用編譯為數值引用,也不確定程序執行過程中的內存布局,而是將這些符號引用信息保留在位元組碼中,由解釋器在運行過程中創立內存布局,然後再通過查表來確定一個方法所在的地址。這樣就有效的保證了Java的可移植性和安全性。
運行JVM位元組碼的工作是由解釋器來完成的。解釋執行過程分三部進行:代碼的裝入、代碼的校驗和代碼的執行。裝入代碼的工作由"類裝載器"(class loader)完成。類裝載器負責裝入運行一個程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調用的類。當類裝載器裝入一個類時,該類被放在自己的名字空間中。除了通過符號引用自己名字空間以外的類,類之間沒有其他辦法可以影響其他類。在本台計算機上的所有類都在同一地址空間內,而所有從外部引進的類,都有一個自己獨立的名字空間。這使得本地類通過共享相同的名字空間獲得較高的運行效率,同時又保證它們與從外部引進的類不會相互影響。當裝入了運行程序需要的所有類後,解釋器便可確定整個可執行程序的內存布局。解釋器為符號引用同特定的地址空間建立對應關系及查詢表。通過在這一階段確定代碼的內存布局,Java很好地解決了由超類改變而使子類崩潰的問題,同時也防止了代碼對地址的非法訪問。
隨後,被裝入的代碼由位元組碼校驗器進行檢查。校驗器可發現操作數棧溢出,非法數據類型轉化等多種錯誤。通過校驗後,代碼便開始執行了。
Java位元組碼的執行有兩種方式:
1.即時編譯方式:解釋器先將位元組碼編譯成機器碼,然後再執行該機器碼。
2.解釋執行方式:解釋器通過每次解釋並執行一小段代碼來完成Java位元組碼程 序的所有操作。
通常採用的是第二種方法。由於JVM規格描述具有足夠的靈活性,這使得將位元組碼翻譯為機器代碼的工作
具有較高的效率。對於那些對運行速度要求較高的應用程序,解釋器可將Java位元組碼即時編譯為機器碼,從而很好地保證了Java代碼的可移植性和高性能。
二.JVM規格描述
JVM的設計目標是提供一個基於抽象規格描述的計算機模型,為解釋程序開發人員提很好的靈活性,同時也確保Java代碼可在符合該規范的任何系統上運行。JVM對其實現的某些方面給出了具體的定義,特別是對Java可執行代碼,即位元組碼(Bytecode)的格式給出了明確的規格。這一規格包括操作碼和操作數的語法和數值、標識符的數值表示方式、以及Java類文件中的Java對象、常量緩沖池在JVM的存儲映象。這些定義為JVM解釋器開發人員提供了所需的信息和開發環境。Java的設計者希望給開發人員以隨心所欲使用Java的自由。
JVM定義了控制Java代碼解釋執行和具體實現的五種規格,它們是:
JVM指令系統
JVM寄存器
JVM棧結構
JVM碎片回收堆
JVM存儲區
2.1JVM指令系統
JVM指令系統同其他計算機的指令系統極其相似。Java指令也是由 操作碼和操作數兩部分組成。操作碼為8位二進制數,操作數進緊隨在操作碼的後面,其長度根據需要而不同。操作碼用於指定一條指令操作的性質(在這里我們採用匯編符號的形式進行說明),如iload表示從存儲器中裝入一個整數,anewarray表示為一個新數組分配空間,iand表示兩個整數的"與",ret用於流程式控制制,表示從對某一方法的調用中返回。當長度大於8位時,操作數被分為兩個以上位元組存放。JVM採用了"big endian"的編碼方式來處理這種情況,即高位bits存放在低位元組中。這同 Motorola及其他的RISC CPU採用的編碼方式是一致的,而與Intel採用的"little endian "的編碼方式即低位bits存放在低位位元組的方法不同。
Java指令系統是以Java語言的實現為目的設計的,其中包含了用於調用方法和監視多先程系統的指令。Java的8位操作碼的長度使得JVM最多有256種指令,目前已使用了160多種操作碼。
2.2JVM指令系統
所有的CPU均包含用於保存系統狀態和處理器所需信息的寄存器組。如果虛擬機定義較多的寄存器,便可以從中得到更多的信息而不必對棧或內存進行訪問,這有利於提高運行速度。然而,如果虛擬機中的寄存器比實際CPU的寄存器多,在實現虛擬機時就會佔用處理器大量的時間來用常規存儲器模擬寄存器,這反而會降低虛擬機的效率。針對這種情況,JVM只設置了4個最為常用的寄存器。它們是:
pc程序計數器
optop操作數棧頂指針
frame當前執行環境指針
vars指向當前執行環境中第一個局部變數的指針
所有寄存器均為32位。pc用於記錄程序的執行。optop,frame和vars用於記錄指向Java棧區的指針。
2.3JVM棧結構
作為基於棧結構的計算機,Java棧是JVM存儲信息的主要方法。當JVM得到一個Java位元組碼應用程序後,便為該代碼中一個類的每一個方法創建一個棧框架,以保存該方法的狀態信息。每個棧框架包括以下三類信息:
局部變數
執行環境
操作數棧
局部變數用於存儲一個類的方法中所用到的局部變數。vars寄存器指向該變數表中的第一個局部變數。
執行環境用於保存解釋器對Java位元組碼進行解釋過程中所需的信息。它們是:上次調用的方法、局部變數指針和操作數棧的棧頂和棧底指針。執行環境是一個執行一個方法的控制中心。例如:如果解釋器要執行iadd(整數加法),首先要從frame寄存器中找到當前執行環境,而後便從執行環境中找到操作數棧,從棧頂彈出兩個整數進行加法運算,最後將結果壓入棧頂。
操作數棧用於存儲運算所需操作數及運算的結果。
2.4JVM碎片回收堆
Java類的實例所需的存儲空間是在堆上分配的。解釋器具體承擔為類實例分配空間的工作。解釋器在為一個實例分配完存儲空間後,便開始記錄對該實例所佔用的內存區域的使用。一旦對象使用完畢,便將其回收到堆中。
在Java語言中,除了new語句外沒有其他方法為一對象申請和釋放內存。對內存進行釋放和回收的工作是由Java運行系統承擔的。這允許Java運行系統的設計者自己決定碎片回收的方法。在SUN公司開發的Java解釋器和Hot Java環境中,碎片回收用後台線程的方式來執行。這不但為運行系統提供了良好的性能,而且使程序設計人員擺脫了自己控制內存使用的風險。
2.5JVM存儲區
JVM有兩類存儲區:常量緩沖池和方法區。常量緩沖池用於存儲類名稱、方法和欄位名稱以及串常量。方法區則用於存儲Java方法的位元組碼。對於這兩種存儲區域具體實現方式在JVM規格中沒有明確規定。這使得Java應用程序的存儲布局必須在運行過程中確定,依賴於具體平台的實現方式。
JVM是為Java位元組碼定義的一種獨立於具體平台的規格描述,是Java平台獨立性的基礎。目前的JVM還存在一些限制和不足,有待於進一步的完善,但無論如何,JVM的思想是成功的。
對比分析:如果把Java原程序想像成我們的C++原程序,Java原程序編譯後生成的位元組碼就相當於C++原程序編譯後的80x86的機器碼(二進製程序文件),JVM虛擬機相當於80x86計算機系統,Java解釋器相當於80x86CPU。在80x86CPU上運行的是機器碼,在Java解釋器上運行的是Java位元組碼。
Java解釋器相當於運行Java位元組碼的「CPU」,但該「CPU」不是通過硬體實現的,而是用軟體實現的。Java解釋器實際上就是特定的平台下的一個應用程序。只要實現了特定平台下的解釋器程序,Java位元組碼就能通過解釋器程序在該平台下運行,這是Java跨平台的根本。當前,並不是在所有的平台下都有相應Java解釋器程序,這也是Java並不能在所有的平台下都能運行的原因,它只能在已實現了Java解釋器程序的平台下運行。
『叄』 C語言是怎麼經過轉換將變數的值存儲在內存中的
C語言編譯器負責將源代碼中的十進制的10翻譯成4位元組數據:0A(16進制)(實際存儲為二進制,但描述起來太長不方便一般用16進制顯示和描述),而一個位元組的10實際上的二進制就是你說的00001010。
實際上在處理存儲之前需要先分配一個內存給變數 i,並在編譯環境中注冊登記這個變數名-地址關系(變數表),方便後續代碼訪問此變數時匹配到該地址,比如遇到 i=i+2時,編譯器就會生成一個指令對該地址的值進行+2操作。此外還需要生成一個MOV指令代碼指揮CPU執行:數據保存到該地址。
如果你對編譯後的細節感興趣,可以在調試時按CTRL+ALT+D(VS的快捷鍵,也可以通過菜單導航到「調試-窗口-反匯編」,其他編譯器在調試菜單中找)切換到反匯編窗口查看編譯結果,系統會逐條顯示每一條語句對應的匯編指令。
當你對匯編指令感到好奇之後,可以看看匯編、編譯原理、8086 CPU原理、以及數字電路的解碼器 等書籍,不需要完全看懂,了解下會對你學習任何編程語言有幫助,不僅僅局限於C語言。
『肆』 C語言哪些函數語句的機器碼會存儲在內存中
自然不會,每一個變數(包括函數)都有自己的生命周期,程序結束後釋放所有的內存,程序運行時只有被聲明為inline內聯函數,或者正在被調用的函數(也就是你上面收提及的第一種情況)存儲在內存中(棧區),調用完畢後立即釋放內存。
回答完畢,祝樓主學習進步。
『伍』 V8編譯生成的機器碼究竟是什麼
實際上V8的JIT編譯器是直接在內存中生成機器碼的,並不會先生成文本形式的匯編然後再使用匯編器去轉換為機器碼。「動態生成機器碼」聽起來可能有點玄乎,其實根本沒啥,就是往內存里寫位元組,這些位元組正好是某些機器碼的意思,然後把這塊內存當作函數去調用就是了。由於代碼自身就是動態生成的,在生成的代碼里直接嵌入resolve好的各種值其實就相當於傳統編譯流程里的「動態鏈接」的效果。順手放倆我以前博客的傳送門:V8實際上自帶一個用C++實現的「匯編器庫」用來動態生成機器碼。它並不把文本形式的匯編轉換為機器碼,而是提供一組C++ API,調用這個API的函數就可以在內存里生成機器碼來。有興趣的同學可能會知道,V8的MacroAssembler庫源自Animorphic的Strongtalk VM,而Strongtalk VM也是HotSpot JVM的前輩。V8 Design Elements文檔里所描述的是最初期的V8的狀態。當時的V8隻有一個JIT編譯器,一個JavaScript函數通常只會被JIT編譯一次。這個JIT編譯器做的優化也不是很多。後來V8演化為擁有兩個JIT編譯器,一個初級編譯器(baseline compiler,名字叫做Full Code Generator,簡稱FullCodeGen),和一個優化編譯器(optimizing compiler,名字叫做Crankshaft),兩個編譯器結合在一次構成雙層編譯。JavaScript函數通常會先被FullCodeGen編譯,然後如果還繼續執行很多次的話則會再被Crankshaft重新編譯一遍,生成更優化的代碼。在這個架構中,FullCodeGen里生成的代碼還是跟V8 Design Elements的相似,會通過inline cache來實現property access;而這些inline cache不但用於實現fast property access,更重要的是它們會被用於收集profile,然後等到Crankshaft編譯的時候,它就可以看先前收集的profile來做profile-guided optimization。以這個 function foo(p) { return p.x } 為例,參數p沒有任何特別的地方,所以JavaScript引擎也無法知道p到底可能有怎樣的值。但通過FullCodeGen生成的代碼所收集到的profile信息,Crankshaft再去編譯 foo() 的時候就可以知道p之前通常指向一個Map(hidden class)為0x2c97ccb179d1的類型的對象。這個類型的constructor為Point、[[Prototype]] 為Point.prototype、對象里有足夠空間容納10個內嵌的欄位(in-object property),並且其中2個slot被用於存儲Smi類型,剩餘的8個slot未被使用。
『陸』 程序如何占內存
這兒我只討論像C和匯編這樣的編譯型語言的問題。解釋型語言的佔用內存情況和編譯語言完全不同。
所有的編譯型語言在執行前最終都要轉換成機器碼。在windows下,.exe的主要進行工作的部分就是機器碼。(像鏈接信息和資源和原始的源程序沒有直接關系)。在執行程序的時候,機器碼會先被讀入內存,然後執行。
像圖片,文本這一類的東西,使用什麼語言都不會改變它們佔用內存的情況。差異就在於機器碼佔用內存的情況。機器碼越多,佔用也越多。
編譯過程其實就是把源程序轉變成機器碼的過程。匯編和機器碼的關系幾乎是一一對應的。寫一句匯編程序,通常就是一個機器碼指令。而像C語言一個語句通常對應很多個機器指令。實現同樣的功能,使用匯編語言一定可以比像C這樣的高級語言實現編譯後相同或更少的機器指令。因為理論上說你可以把編譯好後的機器指令一個一個用匯編「寫」出來。
當然這只是理論上來說,用匯編寫出比C語言編譯出來的機器指令多很多也有可能。
在高級語言中,不同的編譯器編譯相同的程序,生成的機器碼可能完全不同。執行時佔用的內存也不同。匯編程序的編譯出來的幾乎都一樣。
上面的說法用來理解所謂程序佔用內存的問題是可以。但現實的系統中還涉及到不同計算機類型使用不同的機器指令。動態裝入程序,以及虛擬機等等問題。具體程序是如何佔用內存的,不看相當數量的東西是不可能完全理解的。
『柒』 C語言的基本類型在內存中怎麼儲存的
C語言的基本類型在內存中以二進制的形式儲存的。
1、整型數據:所有整數(正負零)在內存中都是以補碼的形式存在。對於一個正整數來說,它的補碼就是它的原碼本身。對於一個負整數來說,它的補碼為原碼取反再加1。
2、字元型數據:把字元的相對應的ASCII碼放到存儲碼單元中,而這些ASCII代碼值在計算機中同樣以二進制補碼的形式存放的。
3、實型數據:也叫浮點數,在計算機中也是以二進制的方式存儲,關鍵在於如何將十進制的小數轉化為二進制來表示。
擴展資料:
根據計算機的內部字長和編譯器的版本,C語言的基本類型表示的數的長度范圍是有限定的。十進制無符號整常數的范圍為0~65535,有符號數為-32768~+32767。八進制無符號數的表示範圍為0~0177777。十六進制無符號數的表示範圍為0X0~0XFFFF或0x0~0xFFFF。
如果使用的數超過了上述范圍,就必須用長整型數來表示。長整型數是用後綴「L」或「l」來表示的。長整數158L和基本整常數158在數值上並無區別。
『捌』 匯編語言編譯器編譯出來的機器碼是不是可以放到任何計算機裡面運行
當然是不行的,用什麼CPU的匯編語言形成的機器碼只能被該類CPU所處硬體環境運行。
『玖』 程序編譯後的機器碼是在內存,還是硬碟
程序編譯後,機器碼是存在硬碟。
在執行時,機器碼,被操作系統,讀入內存。