程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 懂得Java虛擬機JVM的根本構造及JVM的內存溢出方法

懂得Java虛擬機JVM的根本構造及JVM的內存溢出方法

編輯:關於JAVA

懂得Java虛擬機JVM的根本構造及JVM的內存溢出方法。本站提示廣大學習愛好者:(懂得Java虛擬機JVM的根本構造及JVM的內存溢出方法)文章只能為提供參考,不一定能成為您想要的結果。以下是懂得Java虛擬機JVM的根本構造及JVM的內存溢出方法正文


JVM外部構造圖

Java虛擬機重要分為五個區域:辦法區、堆、Java棧、PC存放器、當地辦法棧。上面
來看一些關於JVM構造的主要成績。

1.哪些區域是同享的?哪些是公有的?

Java棧、當地辦法棧、法式計數器是隨用戶線程的啟動和停止而樹立和燒毀的,
每一個線程都有自力的這些區域。而辦法區、堆是被全部JVM過程中的一切線程同享的。

2.辦法區保留甚麼?會被收受接管嗎?

辦法區不是只保留的辦法信息和代碼,同時在一塊叫做運轉經常量池的子區域還
保留了Class文件中常量表中的各類符號援用,和翻譯出來的直接援用。經由過程堆中
的一個Class對象作為接口來拜訪這些信息。

固然辦法區中保留的是類型信息,然則也是會被收受接管的,只不外收受接管的前提比擬刻薄:

(1)該類的一切實例都曾經被收受接管

(2)加載該類的ClassLoader曾經被收受接管

(3)該類的Class對象沒有在任何處所被援用(包含Class.forName反射拜訪)


3.辦法區中常量池的內容不變嗎?

辦法區中的運轉經常量池保留了Class文件中靜態常量池中的數據。除寄存這些編譯時
生成的各類字面量和符號援用外,還包括了翻譯出來的直接援用。但這不代表運轉經常量池
就不會轉變。好比運轉時可以挪用String的intern辦法,將新的字符串常量放入池中。

package com.cdai.jvm; 
 
public class RuntimeConstantPool { 
 
  public static void main(String[] args) { 
 
    String s1 = new String("hello"); 
    String s2 = new String("hello"); 
    System.out.println("Before intern, s1 == s2: " + (s1 == s2)); 
     
    s1 = s1.intern(); 
    s2 = s2.intern(); 
    System.out.println("After intern, s1 == s2: " + (s1 == s2)); 
     
  } 
 
} 


4.一切的對象實例都在堆上分派嗎?

跟著逃逸剖析技巧的逐步成熟,棧上分派、標量調換優化技巧使得“一切對象都分派
在堆上”也變得不那末相對。

所謂逃逸就是當一個對象的指針被多個辦法或線程援用時,我們稱這個指針產生逃逸。
普通來講,Java對象是在堆裡分派的,在棧中只保留了對象的指針。假定一個部分變量
在辦法履行時代未產生逃逸(裸露給辦法外),則直接在棧裡分派,以後持續在挪用棧
裡履行,辦法履行停止後棧空間被收受接管,部分變量就也被收受接管了。如許就削減了年夜量暫時
對象在堆平分配,進步了GC收受接管的效力。

別的,逃逸剖析也會對未產生逃逸的部分變量停止鎖省略,將該變量上具有的鎖省略失落。
啟用逃逸剖析的辦法時加上JVM啟動參數:-XX:+DoEscapeAnalysis?EscapeAnalysisTest。


5.拜訪堆上的對象有幾種方法?

(1)指針直接拜訪

棧上的援用保留的就是指向堆上對象的指針,一次便可以定位對象,拜訪速度比擬快。
然則當對象在堆中被挪動時(渣滓收受接管時會常常挪動各個對象),棧上的指針變量的值
也須要轉變。今朝JVM HotSpot采取的是這類方法。

(2)句柄直接拜訪

棧上的援用指向的是句柄池中的一個句柄,經由過程這個句柄中的值再拜訪對象。是以句柄
就像二級指針,須要兩次定位能力拜訪到對象,速度比直接指針定位要慢一些,然則當
對象在堆中的地位挪動時,不須要轉變棧上援用的值。


JVM內存溢出的方法
懂得了Java虛擬機五個內存區域的感化後,上面我們來持續進修下在甚麼情形下
這些區域會產生溢出。

1.虛擬機參數設置裝備擺設

-Xms:初始堆年夜小,默許為物理內存的1/64(<1GB);默許(MinHeapFreeRatio參數可以調劑)空余堆內存小於40%時,JVM就會增年夜堆直到-Xmx的最年夜限制。

-Xmx:最年夜堆年夜小,默許(MaxHeapFreeRatio參數可以調劑)空余堆內存年夜於70%時,JVM會削減堆直到 -Xms的最小限制。

-Xss:每一個線程的客棧年夜小。JDK5.0今後每一個線程客棧年夜小為1M,之前每一個線程客棧年夜小為256K。應依據運用的線程所需內存年夜小停止恰當調劑。在雷同物理內存下,減小這個值能生成更多的線程。然則操作體系對一個過程內的線程數照樣無限制的,不克不及無窮生成,經歷值在3000~5000閣下。普通小的運用, 假如棧不是很深, 應當是128k夠用的,年夜的運用建議應用256k。這個選項對機能影響比擬年夜,須要嚴厲的測試。

-XX:PermSize:設置永遠代(perm gen)初始值。默許值為物理內存的1/64。

-XX:MaxPermSize:設置耐久代最年夜值。物理內存的1/4。


2.辦法區溢出

由於辦法區是保留類的相干信息的,所以當我們加載過量的類時就會招致辦法區
溢出。在這裡我們經由過程JDK靜態署理和CGLIB署理兩種方法來試圖使辦法區溢出。

2.1 JDK靜態署理

package com.cdai.jvm.overflow; 
 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
 
public class MethodAreaOverflow { 
 
  static interface OOMInterface { 
  } 
   
  static class OOMObject implements OOMInterface { 
  } 
   
  static class OOMObject2 implements OOMInterface { 
  } 
   
  public static void main(String[] args) { 
    final OOMObject object = new OOMObject(); 
    while (true) { 
      OOMInterface proxy = (OOMInterface) Proxy.newProxyInstance( 
          Thread.currentThread().getContextClassLoader(),  
          OOMObject.class.getInterfaces(),  
          new InvocationHandler() { 
            @Override 
            public Object invoke(Object proxy, Method method, Object[] args) 
                throws Throwable { 
              System.out.println("Interceptor1 is working"); 
              return method.invoke(object, args); 
            } 
          } 
      ); 
      System.out.println(proxy.getClass()); 
      System.out.println("Proxy1: " + proxy); 
       
      OOMInterface proxy2 = (OOMInterface) Proxy.newProxyInstance( 
          Thread.currentThread().getContextClassLoader(),  
          OOMObject.class.getInterfaces(),  
          new InvocationHandler() { 
            @Override 
            public Object invoke(Object proxy, Method method, Object[] args) 
                throws Throwable { 
              System.out.println("Interceptor2 is working"); 
              return method.invoke(object, args); 
            } 
          } 
      ); 
      System.out.println(proxy2.getClass()); 
      System.out.println("Proxy2: " + proxy2); 
    } 
  } 
 
} 

固然我們赓續挪用Proxy.newInstance()辦法來創立署理類,然則JVM並沒有內存溢出。
每次挪用都生成了分歧的署理類實例,然則署理類的Class對象沒有轉變。是否是Proxy
類對署理類的Class對象有緩存?詳細緣由會在以後的《JDK靜態署理與CGLIB》中停止
具體剖析。

2.2 CGLIB署理

CGLIB異樣會緩存署理類的Class對象,然則我們可以經由過程設置裝備擺設讓它不緩存Class對象,
如許便可以經由過程重復創立署理類到達使辦法區溢出的目標。

package com.cdai.jvm.overflow; 
 
import java.lang.reflect.Method; 
 
import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 
 
public class MethodAreaOverflow2 { 
 
  static class OOMObject { 
  } 
 
  public static void main(String[] args) { 
    while (true) { 
      Enhancer enhancer = new Enhancer(); 
      enhancer.setSuperclass(OOMObject.class); 
      enhancer.setUseCache(false); 
      enhancer.setCallback(new MethodInterceptor() { 
        @Override 
        public Object intercept(Object obj, Method method, 
            Object[] args, MethodProxy proxy) throws Throwable { 
          return method.invoke(obj, args); 
        } 
      }); 
      OOMObject proxy = (OOMObject) enhancer.create(); 
      System.out.println(proxy.getClass()); 
    } 
  } 
   
} 


3.堆溢出

堆溢出比擬簡略,只需經由過程創立一個年夜數組對象來請求一塊比擬年夜的內存,便可以使
堆產生溢出。

package com.cdai.jvm.overflow; 
 
public class HeapOverflow { 
 
  private static final int MB = 1024 * 1024; 
   
  @SuppressWarnings("unused") 
  public static void main(String[] args) { 
    byte[] bigMemory = new byte[1024 * MB]; 
  } 
 
} 


4.棧溢出

棧溢出也比擬罕見,有時我們編寫的遞歸挪用沒有准確的終止前提時,就會使辦法赓續
遞歸,棧的深度赓續增年夜,終究產生棧溢出。

package com.cdai.jvm.overflow; 
 
public class StackOverflow { 
 
  private static int stackDepth = 1; 
   
  public static void stackOverflow() { 
    stackDepth++; 
    stackOverflow(); 
  } 
   
  public static void main(String[] args) { 
    try { 
      stackOverflow(); 
    }  
    catch (Exception e) { 
      System.err.println("Stack depth: " + stackDepth); 
      e.printStackTrace(); 
    } 
  } 
   
} 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved