91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java虛擬機問題有哪些

發布時間:2021-10-18 15:00:44 來源:億速云 閱讀:146 作者:小新 欄目:移動開發

這篇文章主要介紹了Java虛擬機問題有哪些,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

6.0.0.1 運行時數據區域有哪些?Java虛擬機棧是做什么的?本地方法棧又是做什么的?
  • 運行時數據區域有哪些?

    • 它是方法區的一部分。Class文件中除了有關的版本、字段、方法、接口等描述信息外、還有一項信息是常量池,用于存放編輯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放

    • Java語言并不要求常量一定只有編輯期才能產生,也就是可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法

    • 當常量池無法再申請到內存時會拋出OutOfMemoryError異常

    • 方法區它用于儲存已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據

    • 除了Java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以選擇不實現垃圾收集。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載

    • 當方法區無法滿足內存分配需求時,將拋出OutOfMemoryErroy異常

    • 堆是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動的時候創建,此內存區域的唯一目的是存放對象實例,幾乎所有的對象實例都在這里分配內存。所有的對象實例和數組都在堆上分配

    • Java堆是垃圾收集器管理的主要區域。Java堆細分為新生代和老年代

    • 不管怎樣,劃分的目的都是為了更好的回收內存,或者更快地分配內存

    • Java堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可。如果在堆中沒有完成實例分配,并且堆也無法在擴展時將會拋出OutOfMemoryError異常

    • 本地方法棧和虛擬機棧發揮的作用是非常類似的,他們的區別是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則為虛擬機使用到的Native方法服務

    • 本地方法棧區域也會拋出StackOverflowError和OutOfMemoryErroy異常

    • 虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用于儲存局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。

    • 技術博客大總結

    • 棧內存就是虛擬機棧,或者說是虛擬機棧中局部變量表的部分

    • 局部變量表存放了編輯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(refrence)類型和returnAddress類型(指向了一條字節碼指令的地址)

    • 其中64位長度的long和double類型的數據會占用兩個局部變量空間,其余的數據類型只占用1個。

    • Java虛擬機規范對這個區域規定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常。如果虛擬機擴展時無法申請到足夠的內存,就會跑出OutOfMemoryError異常

    • 程序計數器是一塊較小的內存,他可以看做是當前線程所執行的行號指示器。字節碼解釋器工作的時候就是通過改變這個計數器的值來選取下一條需要執行的字節碼的指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Native方法,這個計數器則為空。此內存區域是唯一一個在Java虛擬機規范中沒有規定任何OutOfMemotyError情況的區域

    • Java虛擬機管理的內存包括幾個運行時數據內存:方法區、虛擬機棧、本地方法棧、堆、程序計數器,其中方法區和堆是由線程共享的數據區,其他幾個是線程隔離的數據區

    • 1.1 程序計數器

    • 1.2 Java虛擬機棧

    • 1.3 本地方法棧

    • 1.4 Java堆

    • 1.5 方法區

    • 1.6 運行時常量池

6.0.0.2 對象的內存布局?對象的訪問定位方式有哪些?使用指針訪問和使用句柄訪問各具有何優勢?
  • 對象的內存布局?

    • a) 儲存對象自身的運行時數據,如哈希碼、GC分帶年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳

    • b) 另一部分是指類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是那個類的實例

    • 在HotSpot虛擬機中,對象在內存中儲存的布局可以分為3塊區域:對象頭、實例數據和對齊填充

    • 對象頭包括兩部分:

  • 對象的訪問定位方式有哪些?

    • Java堆對象的布局就必須考慮如何訪問類型數據的相關信息,而refreence中存儲的直接就是對象的地址

    • Java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址

    • 使用句柄訪問

    • 使用直接指針訪問

  • 使用指針訪問和使用句柄訪問各具有何優勢?

    • 使用句柄訪問優勢:reference中存儲的是穩點的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference本身不需要修改

    • 使用直接指針訪問優勢:速度更快,節省了一次指針定位的時間開銷,由于對象的訪問在Java中非常頻繁,因此這類開銷積少成多后也是一項非常可觀的執行成本

    • 技術博客大總結

6.0.0.3 說一下對象的創建過程?變量創建過程種放在虛擬機哪里?
  • 說一下對象的創建過程?比如:Dog dog= new Dog();

    • 當虛擬機執行到new指令時,它先在常量池中查找“Dog”,看能否定位到Dog類的符號引用;如果能,說明這個類已經被加載到方法區了,則繼續執行。如果沒有,就讓Class Loader先執行類的加載。

    • 然后,虛擬機開始為該對象分配內存,對象所需要的內存大小在類加載完成后就已經確定了。這時候只要在堆中按需求分配空間即可。具體分配內存時有兩種方式,第一種,內存絕對規整,那么只要在被占用內存和空閑內存間放置指針即可,每次分配空間時只要把指針向空閑內存空間移動相應距離即可,當某對象被GC回收后,則需要進行某些對象內存的遷移。第二種,空閑內存和非空閑內存夾雜在一起,那么就需要用一個列表來記錄堆內存的使用情況,然后按需分配內存。

    • 對于多線程的情況,如何確保一個線程分配了對象內存但尚未修改內存管理指針時,其他線程又分配該塊內存而覆蓋的情況?有一種方法,就是讓每一個線程在堆中先預分配一小塊內存(TLAB本地線程分配緩沖),每個線程只在自己的內存中分配內存。但對象本身按其訪問屬性是可以線程共享訪問的。

    • 內存分配到后,虛擬機將分配的內存空間都初始化為零值(不包括對象頭)。實例變量按變量類型初始化相應的默認值(數值型為0,boolan為false),所以實例變量不賦初值也能使用。接著設置對象頭信息,比如對象的哈希值,GC分代年齡等。技術博客大總結

    • 從虛擬機角度,此時一個新的對象已經創建完成了。但從我們程序運行的角度,新建對象才剛剛開始,對象的構造方法還沒有執行。只有執行完構造方法,按構造方法進行初始化后,對象才是徹底創建完成了。構造函數的執行還涉及到調用父類構造器,如果沒有顯式聲明調用父類構造器,則自動添加默認構造器。

    • new運算符可以返回堆中這個對象的引用

  • 變量創建過程種放在虛擬機哪里?

    • 如果dog局部變量,dog變量在棧幀的局部變量表,這個對象的引用就放在棧幀。

    • 如果dog是實例變量,dog變量在堆中,對象的引用就放在堆。

    • 如果dog是靜態變量,dog變量在方法區,對象的引用就放在方法區。

    • 變量是實例變量、局部變量或靜態變量的不同將引用放在不同的地方:

6.0.0.4 OutOfMemoryError異常在哪些數據區域中可能會出現?分別說一下這個數據區域出現OOM的場景和緣由?
  • OutOfMemoryError異常在哪些數據區域中可能會出現?

    • Java堆溢出

    • 虛擬機棧和本地方法棧溢出

    • 方法區和運行時常量池溢出

  • 分別說一下這個數據區域出現OOM的場景和緣由?

    • String.intern()是一個Native方法,它的作用是:如果字符串常量池中已經包含一個等于此String對象的字符串,則返回代表池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用

    • 由于常量池分配在永久代中,可以通過-XX:PermSize和-XX:MaxPermSize限制方法區大小,從而間接限制其中常量池的容量。技術博客大總結

    • Intern():JDK1.6 intern方法會把首次遇到的字符串實例復制到永久代,返回的也是永久代中這個字符串實例的引用,而由StringBuilder創建的字符串實例在Java堆上,所以必然不是一個引用。JDK1.7 intern()方法的實現不會再復制實例,只是在常量池中記錄首次出現的實例引用,因此intern()返回的引用和由StringBuilder創建的那個字符串實例是同一個

    • 對于HotSpot來說,雖然-Xoss參數(設置本地方法棧大小)存在,但實際上是無效的,棧容量只由-Xss參數設定。關于虛擬機棧和本地方法棧,在Java虛擬機規范中描述了兩種異常:

    • 如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError

    • 如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常

    • 在單線程下,無論由于棧幀太大還是虛擬機棧容量太小,當內存無法分配的時候,虛擬機拋出的都是StackOverflowError異常

    • 如果是多線程導致的內存溢出,與棧空間是否足夠大并不存在任何聯系,這個時候每個線程的棧分配的內存越大,反而越容易產生內存溢出異常。解決的時候是在不能減少線程數或更換64為的虛擬機的情況下,就只能通過減少最大堆和減少棧容量來換取更多的線程

    • Java堆用于存儲對象實例,只要不斷的創建對象,并且保證GCRoots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么在數量到達最大堆的容量限制后就會產生內存溢出異常

    • 如果是內存泄漏,可進一步通過工具查看泄漏對象到GC Roots的引用鏈。于是就能找到泄露對象是通過怎樣的路徑與GC Roots相關聯并導致垃圾收集器無法自動回收它們的。掌握了泄漏對象的類型信息及GC Roots引用鏈的信息,就可以比較準確地定位出泄漏代碼的位置

    • 如果不存在泄露,換句話說,就是內存中的對象確實都還必須存活著,那就應當檢查虛擬機的堆參數(-Xmx與-Xms),與機器物理內存對比看是否還可以調大,從代碼上檢查是否存在某些對象生命周期過長、持有狀態時間過長的情況,嘗試減少程序運行期的內存消耗

    • Java堆溢出

    • 虛擬機棧和本地方法棧溢出

    • 方法區和運行時常量池溢出

6.0.0.6 Java中堆和棧的區別?分別寫出堆內存溢出與棧內存溢出的程序?
  • Java中堆和棧的區別?

    • 棧內存:主要用來存放基本數據類型和局部變量;當在代碼塊定義一個變量時會在棧中為這個變量分配內存空間,當超過變量的作用域后這塊空間就會被自動釋放掉。

    • 堆內存:用來存放運行時創建的對象,比如通過new關鍵字創建出來的對象和數組;需要由Java虛擬機的自動垃圾回收器來管理。

  • 分別寫出堆內存溢出與棧內存溢出的程序?

    • 棧內存溢出

      public void A() {
      A();
      }
    • 堆內存溢出

      public void testd() {
      List<String> list = new ArrayList<>();
      int i = 0;
      while (true) {
          list.add(new String(i + ""));
          i++;
      }
      }
6.0.0.7 如果對象的引用被置為null,垃圾收集器是否會立即釋放對象占用的內存?
  • 如果對象的引用被置為null,垃圾收集器是否會立即釋放對象占用的內存?

    • 不會,在下一個垃圾回收周期中,這個對象將是可被回收的。

    • 也就是說當一個對象的引用變為null時,并不會被垃圾收集器立刻回收,而是在下一次垃圾回收時才會釋放其占用的內存。

6.0.0.8 java中垃圾收集的方法有哪些?
  • java中垃圾收集的方法有哪些

    • 現在的虛擬機垃圾收集大多采用這種方式,它根據對象的生存周期,將堆分為新生代和老年代。在新生代中,由于對象生存期短,每次回收都會有大量對象死去,那么這時就采用復制算法。老年代里的對象存活率較高,沒有額外的空間進行分配擔保,所以可以使用標記-整理 或者 標記-清除。

    • 該算法主要是為了解決標記-清除,產生大量內存碎片的問題;當對象存活率較高時,也解決了復制算法的效率問題。它的不同之處就是在清除對象的時候現將可回收對象移動到一端,然后清除掉端邊界以外的對象,這樣就不會產生內存碎片了。

    • 為了解決效率問題,復制算法將可用內存按容量劃分為相等的兩部分,然后每次只使用其中的一塊,當一塊內存用完時,就將還存活的對象復制到第二塊內存上,然后一次性清楚完第一塊內存,再將第二塊上的對象復制到第一塊。但是這種方式,內存的代價太高,每次基本上都要浪費一般的內存。

    • 于是將該算法進行了改進,內存區域不再是按照1:1去劃分,而是將內存劃分為8:1:1三部分,較大那份內存交Eden區,其余是兩塊較小的內存區叫Survior區。每次都會優先使用Eden區,若Eden區滿,就將對象復制到第二塊內存區上,然后清除Eden區,如果此時存活的對象太多,以至于Survivor不夠時,會將這些對象通過分配擔保機制復制到老年代中。(java堆又分為新生代和老年代)

    • 這是垃圾收集算法中最基礎的,根據名字就可以知道,它的思想就是標記哪些要被回收的對象,然后統一回收。這種方法很簡單,但是會有兩個主要問題:1.效率不高,標記和清除的效率都很低;2.會產生大量不連續的內存碎片,導致以后程序在分配較大的對象時,由于沒有充足的連續內存而提前觸發一次GC動作。

    • 標記-清除:

    • 復制算法:

    • 標記-整理技術博客大總結

    • 分代收集

6.0.1.1 如和判斷一個對象是否存活?引用計數法和可達性算法哪個更加好?如何理解一個對象不一定會被回收?

    • 所謂引用計數法就是給每一個對象設置一個引用計數器,每當有一個地方引用這個對象時,就將計數器加一,引用失效時,計數器就減一。當一個對象的引用計數器為零時,說明此對象沒有被引用,也就是“死對象”,將會被垃圾回收.

    • 引用計數法有一個缺陷就是無法解決循環引用問題,也就是說當對象A引用對象B,對象B又引用者對象A,那么此時A,B對象的引用計數器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機都沒有采用這種算法。

    1. 引用計數法

  • 2.可達性算法(引用鏈法)

    • 虛擬機棧中引用的對象

    • 方法區類靜態屬性引用的對象

    • 方法區常量池引用的對象

    • 本地方法棧JNI引用的對象

    • 該算法的思想是:從一個被稱為GC Roots的對象開始向下搜索,如果一個對象到GC Roots沒有任何引用鏈相連時,則說明此對象不可用。

    • 在java中可以作為GC Roots的對象有以下幾種:

  • 如何理解一個對象不一定會被回收?技術博客大總結

    • 雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時,一個對象比不一定會被回收。當一個對象不可達GC Root時,這個對象并不會立馬被回收,而是出于一個死緩的階段,若要被真正的回收需要經歷兩次標記

    • 如果對象在可達性分析中沒有與GCRoot的引用鏈,那么此時就會被第一次標記并且進行一次篩選,篩選的條件是是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法或者已被虛擬機調用過,那么就認為是沒必要的。

    • 如果該對象有必要執行finalize()方法,那么這個對象將會放在一個稱為F-Queue的對隊列中,虛擬機會觸發一個Finalize()線程去執行,此線程是低優先級的,并且虛擬機不會承諾一直等待它運行完,這是因為如果finalize()執行緩慢或者發生了死鎖,那么就會造成F-Queue隊列一直等待,造成了內存回收系統的崩潰。GC對處于F-Queue中的對象進行第二次被標記,這時,該對象將被移除”即將回收”集合,等待回收。

6.0.1.2 Class.forName() 和ClassLoader.loadClass()區別?
  • Class.forName() 和ClassLoader.loadClass()區別?

    • 問到的是反射,但是在底層涉及到了虛擬機的類加載知識。

    • Class.forName() 默認執行類加載過程中的連接與初始化動作,一旦執行初始化動作,靜態變量就會被初始化為程序員設置的值,如果有靜態代碼塊,靜態代碼塊也會被執行

    • ClassLoader.loadClass() 默認只執行類加載過程中的加載動作,后面的動作都不會執行

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java虛擬機問題有哪些”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

保定市| 洪泽县| 台北市| 开阳县| 古蔺县| 神农架林区| 南漳县| 东乌珠穆沁旗| 利川市| 长宁区| 深水埗区| 全州县| 务川| 禹城市| 灵台县| 资溪县| 阿克苏市| 清水河县| 上蔡县| 贵港市| 安岳县| 东乡族自治县| 泰和县| 长寿区| 德庆县| 开江县| 自贡市| 遵义县| 沾化县| 东乡县| 迁安市| 元氏县| 红原县| 六安市| 札达县| 龙门县| 霸州市| 锦屏县| 体育| 怀远县| 南岸区|