Linux的進程內存模型是由用戶空間和內核空間組成。html
在應用程序中,若是直接操做外圍設備,訪問時也不知道其餘程序有沒有在訪問,也不知道哪一段能夠用的,你們你爭我搶的,都亂套了,並且也不安全。因此須要一位管理者--操做系統。操做系統將真實的物理地址隔離起來,給每一個程序分配一段虛擬地址,經過mmap將真實地址和虛擬地址起來,好比虛擬地址是0x00,那麼它真實的物理地址多是0x1c。在真實物理地址它可能不是一段連續的地址,可是在虛擬地址是連續的就能夠了。前端
虛擬空間還能夠進行細分:java
內核空間(進程管理,存儲管理,文件管理,設備管理,網絡系統等)
----------
棧
FileMapping
堆
BSS
Data
text
複製代碼
進程由n個線程組成,在JVM中,又對進程以線程爲單位對內存進行劃分。android
在操做系統看來,JVM是一個程序,而Java程序只是運行在程序上的程序,因此JVM須要模擬程序運行的環境。程序員
(圖片來源:csdn-驍兵)
web
public class Test {
public void test(int b, int a) {
int x = 6;
if (b > 0) {
String str = "VeCharm";
}
int y = a;
int c = y + b;
}
}
----------------
javac Test.java
javap -v Test
----------------
class信息:
Last modified 2019-3-31; size 347 bytes
MD5 checksum b0e2fc2ec7a2d576136a693c77213446
Compiled from "Test.java"
public class com.vecharm.lychee.sample.api.Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
...
{
public com.vecharm.lychee.sample.api.Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public void test(int, int);
descriptor: (II)V
flags: ACC_PUBLIC
Code:
stack=2, locals=6, args_size=3
0: bipush 6
2: istore_3
3: iload_1
4: ifle 11
7: ldc #2 // String VeCharm
9: astore 4
11: iload_2
12: istore 4
14: iload 4
16: iload_1
17: iadd
18: istore 5
20: return
LineNumberTable:
line 7: 0
line 8: 3
line 9: 7
line 11: 11
line 12: 14
line 13: 20
StackMapTable: number_of_entries = 1
frame_type = 252 /* append */
offset_delta = 11
locals = [ int ]
}
SourceFile: "Test.java"
複製代碼
public class Test {
public void test(int b, int a) {
int x = 6;
if (b > 0) {
String str = "VeCharm";
}
int y = a;
int c = y + b;
}
}
----------------
javac Test.java
javap -v Test
----------------
class信息:
...
Constant pool:
#1 = Methodref #4.#14 // java/lang/Object."<init>":()V
#2 = String #15 // VeCharm
#3 = Class #16 // com/vecharm/lychee/sample/api/Test
#4 = Class #17 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 test
#10 = Utf8 (II)V
#11 = Utf8 StackMapTable
#12 = Utf8 SourceFile
#13 = Utf8 Test.java
#14 = NameAndType #5:#6 // "<init>":()V
#15 = Utf8 VeCharm
#16 = Utf8 com/vecharm/lychee/sample/api/Test
#17 = Utf8 java/lang/Object
...
SourceFile: "Test.java" 複製代碼
CONSTANT_Utf8 | UTF-8編碼的Unicode字符串 | |
CONSTANT_Integer | int類型字面值 | |
CONSTANT_Float | float類型字面值 | |
CONSTANT_Long | long類型字面值 | |
CONSTANT_Double | double類型字面值 | |
CONSTANT_Class | 對一個類或接口的符號引用 | |
CONSTANT_String | String類型字面值 | |
CONSTANT_Fieldref | 對一個字段的符號引用 | |
CONSTANT_Methodref | 對一個類中聲明的方法的符號引用 | |
CONSTANT_InterfaceMethodref | 對一個接口中聲明的方法的符號引用 | |
CONSTANT_NameAndType | 對一個字段或方法的部分符號引用 |
看到這想必就已經知道了一個從一個Java文件到內存是如何運做的了。類從加載到虛擬機內存中開始到卸載內存爲止,它的整個生命週期包括:加載,驗證,準備,解析,初始化,使用,和卸載7個階段,其中驗證,準備,解析3個部分被稱爲鏈接。數據庫
加載,驗證,準備,初始化和卸載這5個階段是肯定的,類的加載過程是必須按照順序來,而解析階段這個能夠在初始化以後開始,這是爲了支持運行時綁定(動態綁定)。編程
說到底,編程就是編的只是數據和指令,來總結一下流程。api
在內存模型中,咱們須要重點關注的就是Heap。由於它是由咱們來控制的,處理不當容易發生OOM。內存處理的步驟無非也就三個: 申請,整理,清除。管理內存打個比方就和管理賣戲票的,觀衆臺也就幾十個座位,都是寶貴的資源。vip大戶,裏邊走,直接坐貴席。其餘的買計時票看,每隔必定時間把到時的人清出去,但常常有人到時賴着不走,隔一段時間催他才走。有時候座滿了,只能把到時的賴皮清出去,不想走能夠交錢。有時候人家成羣結隊的買票,天然要調配一下,清理出一些連座的給客戶對吧。若是是一大幫人來看,更是歡迎,vip裏面請。緩存
在Java的堆模型中劃分爲三個區。
管理對象的生命週期
生存仍是毀滅,是經過這個對象到GC Root的可到達性來決定的。能做爲GC Root的對象有四種。
引用類型有四種,強引用,軟引用,弱引用,虛引用。
結語,限於篇幅,只是初略的整理了一下大體的流程,參考《深刻Java虛擬機》等。