激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

香港云服务器
服務器之家 - 編程語言 - JAVA教程 - 了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

2020-03-22 13:12cdai JAVA教程

這篇文章主要介紹了Java虛擬機JVM的基本結構及JVM的內存溢出方式,涉及到Java內存分配相關方面的知識,需要的朋友可以參考下

了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

Java虛擬機主要分為五個區域:方法區、堆、Java棧、PC寄存器、本地方法棧。下面
來看一些關于JVM結構的重要問題。

1.哪些區域是共享的?哪些是私有的?

Java棧、本地方法棧、程序計數器是隨用戶線程的啟動和結束而建立和銷毀的,
每個線程都有獨立的這些區域。而方法區、堆是被整個JVM進程中的所有線程共享的。

了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

2.方法區保存什么?會被回收嗎?

方法區不是只保存的方法信息和代碼,同時在一塊叫做運行時常量池的子區域還
保存了Class文件中常量表中的各種符號引用,以及翻譯出來的直接引用。通過堆中
的一個Class對象作為接口來訪問這些信息。

雖然方法區中保存的是類型信息,但是也是會被回收的,只不過回收的條件比較苛刻:

(1)該類的所有實例都已經被回收

(2)加載該類的ClassLoader已經被回收

(3)該類的Class對象沒有在任何地方被引用(包括Class.forName反射訪問)


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

方法區中的運行時常量池保存了Class文件中靜態常量池中的數據。除了存放這些編譯時
生成的各種字面量和符號引用外,還包含了翻譯出來的直接引用。但這不代表運行時常量池
就不會改變。比如運行時可以調用String的intern方法,將新的字符串常量放入池中。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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采用的是這種方式。

了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

(2)句柄間接訪問

棧上的引用指向的是句柄池中的一個句柄,通過這個句柄中的值再訪問對象。因此句柄
就像二級指針,需要兩次定位才能訪問到對象,速度比直接指針定位要慢一些,但是當
對象在堆中的位置移動時,不需要改變棧上引用的值。

了解Java虛擬機JVM的基本結構及JVM的內存溢出方式

 


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動態代理

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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對象,
這樣就可以通過反復創建代理類達到使方法區溢出的目的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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.堆溢出

堆溢出比較簡單,只需通過創建一個大數組對象來申請一塊比較大的內存,就可以使
堆發生溢出。

?
1
2
3
4
5
6
7
8
9
10
11
12
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.棧溢出

棧溢出也比較常見,有時我們編寫的遞歸調用沒有正確的終止條件時,就會使方法不斷
遞歸,棧的深度不斷增大,最終發生棧溢出。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();
    }
  }
   
}

 

延伸 · 閱讀

精彩推薦
618
主站蜘蛛池模板: 天堂福利电影 | 亚洲成人黄色片 | 男女一边摸一边做羞羞视频免费 | 午夜视频在线观看免费视频 | 国产日产精品一区二区三区四区 | 欧美成人性生活 | 成人男女啪啪免费观看网站四虎 | 国产乱色精品成人免费视频 | www国产免费| 青青草好吊色 | 福利在线播放 | 亚洲一区二区免费视频 | jj视频在线播放 | 最新在线黄色网址 | 国产精品亚洲一区二区三区久久 | 国产福利视频在线观看 | 国产资源在线观看视频 | 国产91在线高潮白浆在线观看 | 欧美一级做一a做片性视频 日韩黄色片免费看 | 一级做a爱性色毛片免费1 | 日本成人一二三区 | 国产一区在线免费 | 久久精品亚洲欧美日韩精品中文字幕 | 99精品国产一区二区三区 | 亚洲国产精品久久久久 | 中文字幕视频在线播放 | 91精品国产91久久久久久吃药 | 国产色视频免费 | 激情久久一区二区 | av在线免费观看中文字幕 | 中国久久久 | 一区二区免费看 | 日日鲁夜夜视频热线播放 | 成人午夜免费在线视频 | 久久久午夜电影 | 麻豆视频在线观看 | 一级黄片毛片免费看 | 欧美黄色大片免费观看 | 亚洲 综合 欧美 动漫 丝袜图 | 日本精品久久久一区二区三区 | 久久99精品国产99久久6男男 |