㈠ jvm高手來啊,xms和xmx一樣出現問題.
這句話里「內存不停的漲」的意思是在Xmx的范圍內內存的佔用會不停的漲,但是一旦漲到達到Xmx值的時候就會進行垃圾回收了,內存分配不會超過這個值的,如果進行垃圾回收後仍然不夠用,就會報內存溢出的錯誤。
附:
JVM申請一塊內存的過程:
A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域
B. 當Eden空間足夠時,內存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收);釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區/OLD區
D. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現」out of memory錯誤」
另:
xms/xmx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可佔用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
㈡ JVM原理是什麼
首先這里澄清兩個概念:JVM實例和JVM執行引擎實例,JVM實例對應了一個獨立運行的Java程序,而JVM執行引擎實例則對應了屬於用戶運行程序的線程;也就是JVM實例是進程級別,而執行引擎是線程級別的。JVM是什麼?—JVM的生命周期JVM實例的誕生:當啟動一個Java程序時,一個JVM實例就產生了,任何一個擁有publicstaticvoidmain(String[]args)函數的class都可以作為JVM實例運行的起點,既然如此,那麼JVM如何知道是運行classA的main而不是運行classB的main呢?這就需要顯式的告訴JVM類名,也就是我們平時運行Java程序命令的由來,如JavaclassAhelloworld,這里Java是告訴os運行SunJava2SDK的Java虛擬機,而classA則指出了運行JVM所需要的類名。JVM實例的運行:main()作為該程序初始線程的起點,任何其他線程均由該線程啟動。JVM內部有兩種線程:守護線程和非守護線程,main()屬於非守護線程,守護線程通常由JVM自己使用,Java程序也可以標明自己創建的線程是守護線程。JVM實例的消亡:當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出。JVM是什麼?—JVM的體系結構粗略分來,JVM的內部體系結構分為三部分,分別是:類裝載器(ClassLoader)子系統,運行時數據區,和執行引擎。下面將先介紹類裝載器,然後是執行引擎,最後是運行時數據區1、類裝載器,顧名思義,就是用來裝載.class文件的。JVM的兩種類裝載器包括:啟動類裝載器和用戶自定義類裝載器,啟動類裝載器是JVM實現的一部分,用戶自定義類裝載器則是Java程序的一部分,必須是ClassLoader類的子類。(下面所述情況是針對SunJDK1.2)動類裝載器:只在系統類(JavaAPI的類文件)的安裝路徑查找要裝入的類用戶自定義類裝載器:系統類裝載器:在JVM啟動時創建,用來在CLASSPATH目錄下查找要裝入的類其他用戶自定義類裝載器:這里有必要先說一下ClassLoader類的幾個方法,了解它們對於了解自定義類裝載器如何裝載.class文件至關重要。(Stringname,bytedata[],intoffset,intlength) (Stringname,bytedata[],intoffset,intlength,);(Stringname) (Classc) defineClass用來將二進制class文件(新類型)導入到方法區,也就是這里指的類是用戶自定義的類(也就是負責裝載類)findSystemClass通過類型的全限定名,先通過系統類裝載器或者啟動類裝載器來裝載,並返回Class對象。ResolveClass:讓類裝載器進行連接動作(包括驗證,分配內存初始化,將類型中的符號引用解析為直接引用),這里涉及到Java命名空間的問題,JVM保證被一個類裝載器裝載的類所引用的所有類都被這個類裝載器裝載,同一個類裝載器裝載的類之間可以相互訪問,但是不同類裝載器裝載的類看不見對方,從而實現了有效的屏蔽。2、執行引擎:它或者在執行位元組碼,或者執行本地方法要說執行引擎,就不得不的指令集,每一條指令包含一個單位元組的操作碼,後面跟0個或者多個操作數。(一)指令集以棧為設計中心,而非以寄存器為中心這種指令集設計如何滿足Java體系的要求:平台無關性:以棧為中心使得在只有很少register的機器上實現Java更便利compiler一般採用stack向連接優化器傳遞編譯的中間結果,若指令集以stack為基礎,則有利於運行時進行的優化工作與執行即時編譯或者自適應優化的執行引擎結合,通俗的說就是使編譯和運行用的數據結構統一,更有利於優化的開展。網路移動性:class文件的緊湊性。安全性:指令集中絕大部分操作碼都指明了操作的類型。(在裝載的時候使用數據流分析期進行一次性驗證,而非在執行每條指令的時候進行驗證,有利於提高執行速度)。(二)執行技術主要的執行技術有:解釋,即時編譯,自適應優化、晶元級直接執行其中解釋屬於第一代JVM,即時編譯JIT屬於第二代JVM,自適應優化(目前Sun的HotspotJVM採用這種技術)則吸取第一代JVM和第二代JVM的經驗,採用兩者結合的方式自適應優化:開始對所有的代碼都採取解釋執行的方式,並監視代碼執行情況,然後對那些經常調用的方法啟動一個後台線程,將其編譯為本地代碼,並進行仔細優化。若方法不再頻繁使用,則取消編譯過的代碼,仍對其進行解釋執行。3、運行時數據區:主要包括:方法區,堆,Java棧,PC寄存器,本地方法棧(1)方法區和堆由所有線程共享堆:存放所有程序在運行時創建的對象方法區:當JVM的類裝載器載入.class文件,並進行解析,把解析的類型信息放入方法區。(2)Java棧和PC寄存器由線程獨享,在新線程創建時間里(3)本地方法棧:存儲本地方法調用的狀態上邊總體介紹了運行時數據區的主要內容,下邊進行詳細介紹,要介紹數據區,就不得不說明JVM中的數據類型。JVM中的數據類型:JVM中基本的數據單元是word,而word的長度由JVM具體的實現者來決定數據類型包括基本類型和引用類型,(1)基本類型包括:數值類型(包括除boolean外的所有的Java基本數據類型),boolean(在JVM中使用int來表示,0表示false,其他int值均表示true)和returnAddress(JVM的內部類型,用來實現finally子句)。(2)引用類型包括:數組類型,類類型,介面類型前邊講述了JVM中數據的表示,下面讓我們輸入到JVM的數據區首先來看方法區:上邊已經提到,方法區主要用來存儲JVM從class文件中提取的類型信息,那麼類型信息是如何存儲的呢?眾所周知,Java使用的是大端序(big?endian:即低位元組的數據存儲在高位內存上,如對於1234,12是高位數據,34為低位數據,則Java中的存儲格式應該為12存在內存的低地址,34存在內存的高地址,x86中的存儲格式與之相反)來存儲數據,這實際上是在class文件中數據的存儲格式,但是當數據倒入到方法區中時,JVM可以以任何方式來存儲它。類型信息:包括class的全限定名,class的直接父類,類類型還是介面類型,類的修飾符(public,等),所有直接父介面的列表,Class對象提供了訪問這些信息的窗口(可通過Class.forName(「」)或instance.getClass()獲得),下面是Class的方法,相信大家看了會恍然大悟,(原來如此J)getName(),getSuperClass(),isInterface(),getInterfaces(),getClassLoader();static變數作為類型信息的一部分保存指向ClassLoader類的引用:在動態連接時裝載該類中引用的其他類指向Class類的引用:必然的,上邊已述該類型的常量池:包括直接常量(String,integer和floatpoint常量)以及對其他類型、欄位和方法的符號引用(注意:這里的常量池並不是普通意義上的存儲常量的地方,這些符號引用可能是我們在編程中所接觸到的變數),由於這些符號引用,使得常量池成為Java程序動態連接中至關重要的部分欄位信息:普通意義上的類型中聲明的欄位方法信息:類型中各個方法的信息編譯期常量:指用final聲明或者用編譯時已知的值初始化的類變數class將所有的常量復制至其常量池或者其位元組碼流中。方法表:一個數組,包括所有它的實例可能調用的實例方法的直接引用(包括從父類中繼承來的)除此之外,若某個類不是抽象和本地的,還要保存方法的位元組碼,操作數棧和該方法的棧幀,異常表。舉例:classLava{ privateintspeed=5; voidflow(){} classVolcano{ publicstaticvoidmain(String[]args){ Lavalava=newLava(); lava.flow(); } } 運行命令JavaVolcano;(1)JVM找到Volcano.class倒入,並提取相應的類型信息到方法區。通過執行方法區中的位元組碼,JVM執行main()方法,(執行時會一直保存指向Vocano類的常量池的指針)(2)Main()中第一條指令告訴JVM需為列在常量池第一項的類分配內存(此處再次說明了常量池並非只存儲常量信息),然後JVM找到常量池的第一項,發現是對Lava類的符號引用,則檢查方法區,看Lava類是否裝載,結果是還未裝載,則查找「Lava.class」,將類型信息寫入方法區,並將方法區Lava類信息的指針來替換Volcano原常量池中的符號引用,即用直接引用來替換符號引用。(3)JVM看到new關鍵字,准備為Lava分配內存,根據Volcano的常量池的第一項找到Lava在方法區的位置,並分析需要多少對空間,確定後,在堆上分配空間,並將speed變數初始為0,並將lava對象的引用壓到棧中(4)調用lava的flow()方法好了,大致了解了方法區的內容後,讓我們來看看堆Java對象的堆實現:Java對象主要由實例變數(包括自己所屬的類和其父類聲明的)以及指向方法區中類數據的指針,指向方法表的指針,對象鎖(非必需),等待集合(非必需),GC相關的數據(非必需)(主要視GC演算法而定,如對於標記並清除演算法,需要標記對象是否被引用,以及是否已調用finalize()方法)。那麼為什麼Java對象中要有指向類數據的指針呢?我們從幾個方面來考慮首先:當程序中將一個對象引用轉為另一個類型時,如何檢查轉換是否允許?需用到類數據其次:動態綁定時,並不是需要引用類型,而是需要運行時類型,這里的迷惑是:為什麼類數據中保存的是實際類型,而非引用類型?這個問題先留下來,我想在後續的讀書筆記中應該能明白指向方法表的指針:這里和C++的VTBL是類似的,有利於提高方法調用的效率對象鎖:用來實現多個線程對共享數據的互斥訪問等待集合:用來讓多個線程為完成共同目標而協調功過。(注意Object類中的wait(),notify(),notifyAll()方法)。Java數組的堆實現:數組也擁有一個和他們的類相關聯的Class實例,具有相同dimension和type的數組是同一個類的實例。數組類名的表示:如[[LJava/lang/Object表示Object[][],[I表示int[],[[[B表示byte[][][]至此,堆已大致介紹完畢,下面來介紹程序計數器和Java棧程序計數器:為每個線程獨有,在線程啟動時創建,若thread執行Java方法,則PC保存下一條執行指令的地址。若thread執行native方法,則Pc的值為undefinedJava棧:Java棧以幀為單位保存線程的運行狀態,Java棧只有兩種操作,幀的壓棧和出棧。每個幀代表一個方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會導致該方法對應的幀出棧和釋放內存。幀的組成:局部變數區(包括方法參數和局部變數,對於instance方法,還要首先保存this類型,其中方法參數按照聲明順序嚴格放置,局部變數可以任意放置),操作數棧,幀數據區(用來幫助支持常量池的解析,正常方法返回和異常處理)。本地方法棧:依賴於本地方法的實現,如某個JVM實現的本地方法借口使用C連接模型,則本地方法棧就是C棧,可以說某線程在調用本地方法時,就進入了一個不受JVM限制的領域,也就是JVM可以利用本地方法來動態擴展本身。相信大家都明白JVM是什麼了吧。原文鏈接: http://www.cnblogs.com/chenzhao/archive/2011/08/14/2137713.html
㈢ jvm的理解
1
JVM內存區域
我們在編寫程序時,經常會遇到OOM(out of Memory)以及內存泄漏等問題。為了避免出現這些問題,我們首先必須對JVM的內存劃分有個具體的認識。JVM將內存主要劃分為:方法區、虛擬機棧、本地方法棧、堆、程序計數器。JVM運行時數據區如下:
1.1
程序計數器
程序計數器是線程私有的區域,很好理解嘛~,每個線程當然得有個計數器記錄當前執行到那個指令。佔用的內存空間小,可以把它看成是當前線程所執行的位元組碼的行號指示器。如果線程在執行Java方法,這個計數器記錄的是正在執行的虛擬機位元組碼指令地址;如果執行的是Native方法,這個計數器的值為空(Undefined)。
此內存區域是唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。
1.2
Java虛擬機棧
與程序計數器一樣,Java虛擬機棧也是線程私有的,其生命周期與線程相同。
如何理解虛擬機棧呢?
本質上來講,就是個棧。裡面存放的元素叫棧幀,棧幀好像很復雜的樣子,其實它很簡單!它裡面存放的是一個函數的上下文,具體存放的是執行的函數的一些數據。執行的函數需要的數據無非就是局部變數表(保存函數內部的變數)、操作數棧(執行引擎計算時需要),方法出口等等。
執行引擎每調用一個函數時,就為這個函數創建一個棧幀,並加入虛擬機棧。換個角度理解,每個函數從調用到執行結束,其實是對應一個棧幀的入棧和出棧。
注意這個區域可能出現的兩種異常:
一種是StackOverflowError,當前線程請求的棧深度大於虛擬機所允許的深度時,會拋出這個異常。製造這種異常很簡單:將一個函數反復遞歸自己,最終會出現棧溢出錯誤(StackOverflowError)。
另一種異常是OutOfMemoryError異常,當虛擬機棧可以動態擴展時(當前大部分虛擬機都可以),如果無法申請足夠多的內存就會拋出OutOfMemoryError,如何製作虛擬機棧OOM呢,參考一下代碼:
這段代碼有風險,可能會導致操作系統假死,請謹慎使用~~~
1.3
本地方法棧
本地方法棧與虛擬機所發揮的作用很相似,他們的區別在於虛擬機棧為執行Java代碼方法服務,而本地方法棧是為Native方法服務。與虛擬機棧一樣,本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。
1.4
Java堆
Java堆可以說是虛擬機中最大一塊內存了。它是所有線程所共享的內存區域,幾乎所有的實例對象都是在這塊區域中存放。當然,隨著JIT編譯器的發展,所有對象在堆上分配漸漸變得不那麼「絕對」了。
Java堆是垃圾收集器管理的主要區域。由於現在的收集器基本上採用的都是分代收集演算法,所有Java堆可以細分為:新生代和老年代。在細致分就是把新生代分為:Eden空間、From Survivor空間、To Survivor空間。當堆無法再擴展時,會拋出OutOfMemoryError異常。
1.5
方法區
方法區存放的是類信息、常量、靜態變數等。方法區是各個線程共享區域,很容易理解,我們在寫Java代碼時,每個線程度可以訪問同一個類的靜態變數對象。由於使用反射機制的原因,虛擬機很難推測那個類信息不再使用,因此這塊區域的回收很難。另外,對這塊區域主要是針對常量池回收,值得注意的是JDK1.7已經把常量池轉移到堆裡面了。同樣,當方法區無法滿足內存分配需求時,會拋出OutOfMemoryError。
製造方法區內存溢出,注意,必須在JDK1.6及之前版本才會導致方法區溢出,原因後面解釋,執行之前,可以把虛擬機的參數-XXpermSize和-XX:MaxPermSize限制方法區大小。
運行後會拋出java.lang.OutOfMemoryError:PermGen space異常。
解釋一下,String的intern()函數作用是如果當前的字元串在常量池中不存在,則放入到常量池中。上面的代碼不斷將字元串添加到常量池,最終肯定會導致內存不足,拋出方法區的OOM。
下面解釋一下,為什麼必須將上面的代碼在JDK1.6之前運行。我們前面提到,JDK1.7後,把常量池放入到堆空間中,這導致intern()函數的功能不同,具體怎麼個不同法,且看看下面代碼:
這段代碼在JDK1.6和JDK1.7運行的結果不同。
JDK1.6結果是:false,false ,JDK1.7結果是true, false。
原因是:JDK1.6中,intern()方法會吧首次遇到的字元串實例復制到常量池中,返回的也是常量池中的字元串的引用,而StringBuilder創建的字元串實例是在堆上面,所以必然不是同一個引用,返回false。
在JDK1.7中,intern不再復制實例,常量池中只保存首次出現的實例的引用,因此intern()返回的引用和由StringBuilder創建的字元串實例是同一個。為什麼對str2比較返回的是false呢?這是因為,JVM中內部在載入類的時候,就已經有"java"這個字元串,不符合「首次出現」的原則,因此返回false。
㈣ java對象靜態變數放在jvm的什麼區域
從JVMS角度而言是在方法區(Method Area),對於特定的實現的話,例如Oracle HotSpot而言是在持久區(Permanent Area),HotSpot後續版本中會移除掉持久區的概念。 對於JVM內存區的劃分可參考 不會,因為類的靜態變數是此類的所有實例化對象全局共享的,即指向同一塊內存,只要一個對象更改了靜態變數,其他對象讀到的都是更改後的靜態變數,從設計上而言,也很容易理解,靜態變數也稱之為類變數,可以直接使用類名訪問而不需要通過類對象訪問,一樓所言有誤。 這要看你的業務需求,原則上是盡量減少不必要的靜態變數,對於一直常量類的話,你的變數就要包含靜態變數了。
㈤ JVM是如何工作的呢
Java虛擬機
一、什麼是Java虛擬機
Java虛擬機是一個想像中的機器,在實際的計算機上通過軟體模擬來實現。Java虛擬機有自己想像中的硬體,如處理器、堆棧、寄存器等,還具有相應的指令系統。
1.為什麼要使用Java虛擬機
Java語言的一個非常重要的特點就是與平台的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平台上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機後,Java語言在不同平台上運行時不需要重新編譯。Java語言使用模式Java虛擬機屏蔽了與具體平台相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(位元組碼),就可以在多種平台上不加修改地運行。Java虛擬機在執行位元組碼時,把位元組碼解釋成具體平台上的機器指令執行。
2.誰需要了解Java虛擬機
Java虛擬機是Java語言底層實現的基礎,對Java語言感興趣的人都應對Java虛擬機有個大概的了解。這有助於理解Java語言的一些性質,也有助於使用Java語言。對於要在特定平台上實現Java虛擬機的軟體人員,Java語言的編譯器作者以及要用硬體晶元實現Java虛擬機的人來說,則必須深刻理解Java虛擬機的規范。另外,如果你想擴展Java語言,或是把其它語言編譯成Java語言的位元組碼,你也需要深入地了解Java虛擬機。
3.Java虛擬機支持的數據類型
Java虛擬機支持Java語言的基本數據類型如下:
byte://1位元組有符號整數的補碼
short://2位元組有符號整數的補碼
int://4位元組有符號整數的補碼
long://8位元組有符號整數的補碼
float://4位元組IEEE754單精度浮點數
double://8位元組IEEE754雙精度浮點數
char://2位元組無符號Unicode字元
幾乎所有的Java類型檢查都是在編譯時完成的。上面列出的原始數據類型的數據在Java執行時不需要用硬體標記。*作這些原始數據類型數據的位元組碼(指令)本身就已經指出了*作數的數據類型,例如iadd、ladd、fadd和dadd指令都是把兩個數相加,其*作數類型別是int、long、 float和double。虛擬機沒有給boolean(布爾)類型設置單獨的指令。boolean型的數據是由integer指令,包括integer 返回來處理的。boolean型的數組則是用byte數組來處理的。虛擬機使用IEEE754格式的浮點數。不支持IEEE格式的較舊的計算機,在運行 Java數值計算程序時,可能會非常慢。
虛擬機支持的其它數據類型包括:
object//對一個Javaobject(對象)的4位元組引用
returnAddress//4位元組,用於jsr/ret/jsr-w/ret-w指令
注:Java數組被當作object處理。
虛擬機的規范對於object內部的結構沒有任何特殊的要求。在Sun公司的實現中,對object的引用是一個句柄,其中包含一對指針:一個指針指向該object的方法表,另一個指向該object的數據。用Java虛擬機的位元組碼表示的程序應該遵守類型規定。Java虛擬機的實現應拒絕執行違反了類型規定的位元組碼程序。Java虛擬機由於位元組碼定義的限制似乎只能運行於32位地址空間的機器上。但是可以創建一個Java虛擬機,它自動地把位元組碼轉換成64位的形式。從Java虛擬機支持的數據類型可以看出,Java對數據類型的內部格式進行了嚴格規定,這樣使得各種Java虛擬機的實現對數據的解釋是相同的,從而保證了Java的與平台無關性和可
移植性。
二、Java虛擬機體系結構
Java虛擬機由五個部分組成:一組指令集、一組寄存器、一個棧、一個無用單元收集堆(Garbage-collected-heap)、一個方法區域。這五部分是Java虛擬機的邏輯成份,不依賴任何實現技術或組織方式,但它們的功能必須在真實機器上以某種方式實現。
1.Java指令集
Java虛擬機支持大約248個位元組碼。每個位元組碼執行一種基本的CPU運算,例如,把一個整數加到寄存器,子程序轉移等。Java指令集相當於Java程序的匯編語言。
Java指令集中的指令包含一個單位元組的*作符,用於指定要執行的*作,還有0個或多個*作數,提供*作所需的參數或數據。許多指令沒有*作數,僅由一個單位元組的*作符構成。 虛擬機的內層循環的執行過程如下:
do{
取一個*作符位元組;
根據*作符的值執行一個動作;
}while(程序未結束)
由於指令系統的簡單性,使得虛擬機執行的過程十分簡單,從而有利於提高執行的效率。指令中*作數的數量和大小是由*作符決定的。如果*作數比一個位元組大,那麼它存儲的順序是高位位元組優先。例如,一個16位的參數存放時佔用兩個位元組,其值為:
第一個位元組*256+第二個位元組位元組碼指令流一般只是位元組對齊的。指令tableswitch和lookup是例外,在這兩條指令內部要求強制的4位元組邊界對齊。
2.寄存器
Java虛擬機的寄存器用於保存機器的運行狀態,與微處理器中的某些專用寄存器類似。
Java虛擬機的寄存器有四種:
pc:Java程序計數器。
optop:指向*作數棧頂端的指針。
frame:指向當前執行方法的執行環境的指針。
vars:指向當前執行方法的局部變數區第一個變數的指針。
Java虛擬機
Java虛擬機是棧式的,它不定義或使用寄存器來傳遞或接受參數,其目的是為了保證指令集的簡潔性和實現時的高效性(特別是對於寄存器數目不多的處理器)。
所有寄存器都是32位的。
3.棧
Java虛擬機的棧有三個區域:局部變數區、運行環境區、*作數區。
(1)局部變數區 每個Java方法使用一個固定大小的局部變數集。它們按照與vars寄存器的字偏移量來定址。局部變數都是32位的。長整數和雙精度浮點數占據了兩個局部變數的空間,卻按照第一個局部變數的索引來定址。(例如,一個具有索引n的局部變數,如果是一個雙精度浮點數,那麼它實際占據了索引n和n+1所代表的存儲空間。)虛擬機規范並不要求在局部變數中的64位的值是64位對齊的。虛擬機提供了把局部變數中的值裝載到*作數棧的指令, 也提供了把*作數棧中的值寫入局部變數的指令。
(2)運行環境區 在運行環境中包含的信息用於動態鏈接,正常的方法返回以及異常傳播。
·動態鏈接
運行環境包括對指向當前類和當前方法的解釋器符號表的指針,用於支持方法代碼的動態鏈接。方法的class文件代碼在引用要調用的方法和要訪問的變數時使用符號。動態鏈接把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,並把變數訪問翻譯成與這些變數運行時的存儲結構相應的偏移地址。動態鏈接方法和變數使得方法中使用的其它類的變化不會影響到本程序的代碼。
·正常的方法返回
如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個返回值。執行環境在正常返回的情況下用於恢復調用者的寄存器,並把調用者的程序計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然後在調用者的執行環境中繼續執行下去。
·異常和錯誤傳播
異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:①動態鏈接錯,如無法找到所需的class文件。②運行時錯,如對一個空指針的引用
·程序使用了throw語句。
當異常發生時,Java虛擬機採取如下措施:
·檢查與當前方法相聯系的catch子句表。每個catch子句包含其有效指令范圍,能夠處理的異常類型,以及處理異常的代碼塊地址。
·與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令范圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那麼系統轉移到指定的異常處理塊處執行;如果沒有找到異常處理塊,重復尋找匹配的catch子句的過程,直到當前方法的所有嵌套的 catch子句都被檢查過。
·由於虛擬機從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因為Java代碼是結構化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程序計數器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計數器值下發生的異常情況。
·如果找不到匹配的catch子句,那麼當前方法得到一個"未截獲異常"的結果並返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的異常處理塊,那麼這種錯誤傳播將被繼續下去。如果錯誤被傳播到最頂層,那麼系統將調用一個預設的異常處理塊。
(3)*作數棧區 機器指令只從*作數棧中取*作數,對它們進行*作,並把結果返回到棧中。選擇棧結構的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上,也能夠高效地模擬虛擬機的行為。*作數棧是32位的。它用於給方法傳遞參數,並從方法接收結果,也用於支持*作的參數,並保存*作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是*作數棧頂的兩個字。這兩個字是由先前的指令壓進堆棧的。這兩個整數將從堆棧彈出、相加,並把結果壓回到*作數棧中。
每個原始數據類型都有專門的指令對它們進行必須的*作。每個*作數在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。* 作數只能被適用於其類型的*作符所*作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的虛擬機實現中,這個限制由位元組碼驗證器強制實行。但是,有少數*作(*作符pe和swap),用於對運行時數據區進行*作時是不考慮類型的。
4.無用單元收集堆
Java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java語言具有無用單元收集能力:它不給程序員顯式釋放對象的能力。Java不規定具體使用的無用單元收集演算法,可以根據系統的需求使用各種各樣的演算法。
5.方法區
方法區與傳統語言中的編譯後代碼或是Unix進程中的正文段類似。它保存方法代碼(編譯後的java代碼)和符號表。在當前的Java實現中,方法代碼不包括在無用單元收集堆中,但計劃在將來的版本中實現。每個類文件包含了一個Java類或一個Java界面的編譯後的代碼。可以說類文件是Java 語言的執行代碼文件。為了保證類文件的平台無關性,Java虛擬機規范中對類文件的格式也作了詳細的說明。其具體細節請參考Sun公司的Java虛擬機規范。
內容來源於網上。
㈥ 深入理解jvm原理之逃逸分析
最近一直在學習Java虛擬機原理,覺得有意思的地方就寫個文章記錄下來。優勝劣汰是自然界的發展,適用到Java虛擬機也不為過,jvm過了生存下去,一直在自我進化,Java虛擬機也在不停的進化和優化,有的是基於執行代碼的優化,例如指令重排序等等;有的是基於分析技術,例如關系分析或者逃逸分析等等,今天就重點介紹一下jvm中的分析技術優化---逃逸分析;內容大部分源自於《深入理解Java虛擬機》;
逃逸分析一般分為兩種:一種基本行為就是分析對象的動態作用域,當一個對象在方法中定義後,它可能被外部的方法所引用,例如作為調用參數傳入了其他對象中,稱為方法逃逸;甚至被外部線程所引用,例如賦值給變數或可以在其他線程中訪問的變數,這種優化行為稱為線程逃逸;
概念歸概念,最終效果怎麼樣,肯定還需要是騾子是馬拉出來遛遛,總牛的理論需要落地檢驗,說程序員的話,也就是一個對象不會逃逸到方法或者線程之外後,這個變數會進行一些高效的優化;實現方式一般有下面幾種;
棧上分配:無論是C#還是Java的程序員,大家都知道,對象會創建在Java堆上,而Java堆中的對象對線程(Java線程)是共享和可見的,而虛擬機的垃圾回收就是回收對象不再適用的對象,無論哪種垃圾回收器,都需要需要篩選和整理可回收的對象,回收和整理要耗費很長時間,如果確定一個方法不會逃逸出方法之外,那就讓這個對象直接分配在棧上,而對象所佔用的空間也會隨著幀棧的出棧而銷毀,垃圾回收系統的壓力會就變的小了;
消除同步:線程同步本身就是一個相對耗時的過程(至於為什麼耗時,可以查詢用戶線程和內核線程相關知識),如果確認一個對象不會被其他線程訪問;那麼變數的讀寫就不會和其他線程競爭,對於這種變數實施的同步可以消除;
標量替換:標量又稱scalar是指一個數據已經無法再分解成更小的數據來表示了,Java虛擬機中的原始數據例如int,long,等值類型以及reference類型,都不能再進一步分解,他們就可以稱為標量,相對的,它們如果可以繼續分解,那就是稱為聚合量又稱Aggregate,Java對象就是典型的聚合量,如果把一個對象拆散,根據程序訪問情況,將其使用到的成員變數類型變成基本類型代替,如果jvm逃逸分析中發現這個對象不會外部對象使用,那程序執行的就不會創建該對象,為改為創建它的若干個被這個方法使用的成員變數來代替(棧上創建的數據,又很大的概率會被jvm分配至物理機的高速寄存器中存儲),這個也為後續進一步的優化創造了條件;
逃逸分析很多優勢還在陸陸續續發現,Java8已經默認開啟了逃逸分析, -XX:+DoEscapeAnalysis 開啟或者關閉這個選項;都是幹活,後續上帶么和截圖來驗證一下;
㈦ 請問兩年半的JAVA程序員面試會遇到哪些問題
J2SE基礎:
1. 九種基本數據類型的大小,以及他們的封裝類。
2. Switch能否用string做參數?
3. equals與==的區別。
4. Object有哪些公用方法?
5. Java的四種引用,強弱軟虛,用到的場景。
6. Hashcode的作用。
7. ArrayList、LinkedList、Vector的區別。
8. String、StringBuffer與StringBuilder的區別。
9. Map、Set、List、Queue、Stack的特點與用法。
10. HashMap和HashTable的區別。
11. HashMap和ConcurrentHashMap的區別,HashMap的底層源碼。
12. TreeMap、HashMap、LindedHashMap的區別。
13. Collection包結構,與Collections的區別。
14. try catch finally,try里有return,finally還執行么?
15. Excption與Error包結構。OOM你遇到過哪些情況,SOF你遇到過哪些情況。
16. Java面向對象的三個特徵與含義。
17. Override和Overload的含義去區別。
18. Interface與abstract類的區別。
19. Static class 與non static class的區別。
20. java多態的實現原理。
21. 實現多線程的兩種方法:Thread與Runable。
22. 線程同步的方法:sychronized、lock、reentrantLock等。
23. 鎖的等級:方法鎖、對象鎖、類鎖。
24. 寫出生產者消費者模式。
25. ThreadLocal的設計理念與作用。
26. ThreadPool用法與優勢。
27. Concurrent包里的其他東西:ArrayBlockingQueue、CountDownLatch等等。
28. wait()和sleep()的區別。
29. foreach與正常for循環效率對比。
30. Java IO與NIO。
31. 反射的作用於原理。
32. 泛型常用特點,List<String>能否轉為List<Object>。
33. 解析XML的幾種方式的原理與特點:DOM、SAX、PULL。
34. Java與C++對比。
35. Java1.7與1.8新特性。
36. 設計模式:單例、工廠、適配器、責任鏈、觀察者等等。
37. JNI的使用。
JVM:
1. 內存模型以及分區,需要詳細到每個區放什麼。
2. 堆裡面的分區:Eden,survival from to,老年代,各自的特點。
3. 對象創建方法,對象的內存分配,對象的訪問定位。
4. GC的兩種判定方法:引用計數與引用鏈。
5. GC的三種收集方法:標記清除、標記整理、復制演算法的原理與特點,分別用在什麼地方,如果讓你優化收集方法,有什麼思路?
6. GC收集器有哪些?CMS收集器與G1收集器的特點。
7. Minor GC與Full GC分別在什麼時候發生?
8. 幾種常用的內存調試工具:jmap、jstack、jconsole。
9. 類載入的五個過程:載入、驗證、准備、解析、初始化。
10. 雙親委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。
11. 分派:靜態分派與動態分派。
(來源:面試心得與總結---BAT、網易、蘑菇街)
總體來說java考察內容包括以下這些:
1,面向對象的一些基本概念:繼承,多態之類的
2, 抽象類和介面
3, 靜態類,內部類
4, Java集合類,同步和非同步
5, Java類載入機制
6, Java內存模型和垃圾回收演算法
7, 線程同步機制(voliate,synchronized,重入鎖,threadlocal),線程間通信(wait,notify)
8, 異常處理
9, 多線程同步問題,生產者消費者,讀者寫者,哲學家就餐,用java實現
10, 了解java中設計模式的思想,用了哪些設計模式,有什麼好處
作者:Doing
鏈接:https://www.hu.com/question/29800631/answer/109486025
來源:知乎
㈧ 對象和內存溢出怎麼處理
1. 對象。
A.創建。首先檢查指令的參數能不能在常量區找到類的符號引用,並檢查這個類是否載入、解析和初始化過,如果沒有就執行類的載入過程。其次是內存分配,類載入之後就知道要分配的內存大小,分配方法有兩種,一種是指針碰撞,就是一塊內存是使用過的,一塊是未使用的,用一個指針分割,新分配的內存指針就向空閑的挪動,compact功能的虛擬機是用指針碰撞;另一種是空閑列表,就是一個列表記錄空閑的內存塊,不斷更新列表,新分配的內存在列表中尋找一個合適大小的內存塊,sweep功能的虛擬機是使用空閑列表。第三,在分配內存空間的時候,還要考慮並發性。有兩個方法,一種是同步處理,如採用CAS和失敗重試的方法;另外一種是把內存分配動作按照線程劃分在不同的空間之中,每個線程在堆中預先分配一小塊內存,本地線程分配緩沖TLAB,那個線程需要分配內存在那個TLAB上分配,只有TLAB用完了,才要同步鎖定,重新分配。第四、對對象進行必要設置,比方說對象屬於那個類,如何找到類的元數據信息和對象hashcode以及對象GC分代年齡等。
B.對象的內存布局。分為對象頭、實例數據和對齊填充。對象頭包括兩部分,第一部分是存儲對象自身信息,如hashcode,GC分代年齡,鎖狀態等;第二部分是類型指針,對象指向它的類的元數據的指針,虛擬機通過這個指針確定這是那個類的實例。
C.對象訪問定位。兩種方式,一種是句柄訪問,句柄池有訪問對象實例數據的指針和訪問對象數據類型的指針。這個訪問最大好處是reference是穩定的句柄池地址,對象改變都是改變句柄池裡面的指針,而reference本身不動。另外一種就是直接指針,它有到對象類型數據的指針和實例數據。這個訪問的好處是速度更快,節省了一次指針定位的開銷。
2. 內存溢出OOM。
A.堆溢出。堆存放的是對象實例,只要不斷創建對象,並且保證GC Root到對象有可大路徑避免被垃圾回收清除掉對象,那麼對象數量達到最大堆容量限制就會OOM。用內存映象分析工具,Eclipse Memory Analyzer分析一下。
B.虛擬機棧和本地方法棧溢出。分為兩種,一種是如果線程請求的棧深度大於虛擬機所允許的最大深度,拋出StackOverFlowError異常;另一種是如果虛擬機在擴展棧時無法申請到足夠內存空間,拋出OutOfMemoryError異常。可以減小最大堆和棧容量來獲取更多的線程數量。
C.方法區和常量池溢出。會有額外提示 PermGen space。
D.本機直接內存溢出。這個Heap Dump文件看不到內存佔用,但是如果有直接或簡介使用了NIO,那有可能就是本機直接內存溢出了。
㈨ JVM 內存分哪⼏個區,每個區的作⽤是什麼
從JVM的觀點來看,它位於方法區域,對於具體的實現,如Oracle熱點,它位於永久區域,持久性的概念在熱點的後續版本中被刪除。
JVM內存區域的分區不能被引用,因為類的靜態變數是由類的所有實例化對象(即指向同一塊內存)全局共享的。
只要一個對象改變靜態變數,其他對象就讀取改變的靜態變數。
從設計的角度來看,靜態變數也很容易理解。
也稱為類變數,可以直接使用類名訪問,而不需要通過類對象訪問,第一層是錯誤的。
這取決於您的業務需求,原則上最小化不必要的靜態變數。
對於常數類,變數包含靜態變數。