1. 程式人生 > 實用技巧 >Java虛擬機器學習

Java虛擬機器學習

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

1.java虛擬機器管理記憶體區域劃分

140429_xpyh_2554810.png

由圖可見,方法區(Method Area)和堆(Heap)是多執行緒共享的;而虛擬機器的棧(VM Stack)、執行緒本地棧和程式計數器是各執行緒相互隔離私有的。

2.各區域功能劃分:

2.1程式計數器:

在虛擬機器的概念模型裡,當位元組碼直譯器工作的時候,就是通過改變私有的程式計數器的值來獲取下一條需要執行的位元組碼指令,無論是分支switch、迴圈for、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴該計數器。

因為java虛擬機器的多執行緒是通過執行緒輪流切換並分配處理器執行時間來實現,所以就需要各個執行緒就需要一個私有的程式計數器在能線上程切換後恢復到正確的執行位置。

2.2虛擬機器棧和本地方法棧

虛擬機器棧是為虛擬機器執行java方法服務(每個方法在執行時會建立一個棧幀用於儲存區域性變量表、操作棧數、動態連結、方法出口燈資訊。每個方法從呼叫到執行完成就對應一個棧幀在虛擬機器中入棧到出棧的過程);本地方法棧是為虛擬機器使用native方法服務。

Hotspot虛擬機器並不區分虛擬機器棧和本地方法棧。

二者會丟擲2種異常:StackOutflowError和OutOfMemoryError異常。

2.3Java堆

堆(Heap)是java虛擬機器管理記憶體中最大的一塊,堆被所有的執行緒共享,在虛擬機器啟動時被建立。堆的唯一目的在於存放物件例項

,(幾乎)所有的物件例項都在堆中被分配記憶體。

堆是垃圾收集器管理的主要區域。從記憶體回收的角度來說,垃圾收集器目前主要採用分代收集演算法。從記憶體分配的角度來說,執行緒共享的堆可能劃分出多個執行緒私有的分配快取區(Thread Local Allocation Buffer , TLAB),但無論如何劃分,在堆中存放的都是物件例項。

若堆中無可用記憶體進行分配且堆無法在拓展的時候,會丟擲OutOfMemoryException;

2.4方法區

方法區(Method Area)同堆一樣是為所有執行緒共享的記憶體區域,用於儲存已被虛擬機器載入的類資訊常量靜態變數即時編譯器編譯後的程式碼

等資料。

方法區的垃圾收集主要針對常量池的回收以及對型別的解除安裝。但java虛擬機器對方法區的規範十分寬鬆可以選擇不實現垃圾收集。

2.5執行時常量池

常量池用於存放編譯器生成的各種字面量和符號引用,這部分在類載入後進入方法區的執行時常量池存放。

以下為關於常量池的一些例子:

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;

System.out.println(s1 == s2); // true ,因為s1、s2在賦值時,均使用的字串字面量,就是直接把字串寫死,在編譯期間,這種字面量會直接放入class檔案的常量池中,從而實現複用,載入執行時常量池後,s1、s2指向的是同一個記憶體地址,所以相等。
System.out.println(s1 == s3); // true ,雖然s3是拼接而成的,但拼接的兩部分內容都是已確認的字面量,所以虛擬機器在編譯時會進行內部排序,優化為String s3 = "Hello";
System.out.println(s1 == s4); // false , 雖然s4也是拼接,但new String("lo")這部分不是已知字面量,只有當執行時才會確認具體值,所以編譯器不會對其進行優化
System.out.println(s1 == s9); // false , 同樣s7和s8雖然是已知字面值,但拼接時作為s9的變數並不是確認值,所以二者拼接後的地址也不確定
System.out.println(s4 == s5); // false , 二者在堆中的地址就不一致
System.out.println(s1 == s6); // true , String.intern()方法嘗試將該字串物件新增到常量池中,若常量池中已存在一個該字串(用equals方法判定)則返回常量池中該字串的地址,所以這裡為true。

除了字串常量池外,還有整型常量池、浮點型常量池等等,但數值型常量池不允許手動新增常量,以整形常量池為例,其內常量池的值範圍為-128~127。

如:

Integer i1 = new Integer("100");
Integer i2 = new Integer("100");
System.out.println(i1 == i2);//false,因為用了new關鍵字,jvm在堆中分別開了2塊區域指向i1和i2兩個物件,所以i1和i2的地址不一致,這裡返回false

Integer i3 = 100;
Integer i4 = 100;
System.out.println(i3 == i4);//true,這裡100存在於整型常量池,所以返回同一個物件,為true

Integer i5 = 1000;
Integer i6 = 1000;
System.out.println(i5 == i6);//false,根據integer的原始碼,因為1000超過了整型常量池的範圍,所以這裡jvm分別新建了兩個物件,同i1和i2,返回false

轉載於:https://my.oschina.net/betteru/blog/1541351