(1)在java中,分配內存和回收內存都由JVM自動完成,甚至不須要寫和內存相關的代碼
(2)物理內存即RAM還有寄存器(一種存儲單元,用於存儲計算機單元執行指令(如整形浮點等運算)的中間結果)是處理器經過地址總線鏈接的。地址總線:其寬度決定了一次能夠存寄存器或者RAM中獲取多少個bit和處理器最大的能夠尋址的範圍,每一個地址會引用一個字節,因此若是是32位的總線則能夠有4G的內存空間。(一般狀況下地址總線和RAM或寄存器有相同的位數)
(3)一般操做系統的內存申請空間是按照進程來管理的,每一個進程間不會互相重合,操做系統保證每一個進程擁有一段獨立的地址空間。(邏輯上獨立,物理空間不必定獨立,如虛擬內存,虛擬內存是計算機系統內存管理的一種技術。它使得應用程序認爲它擁有連續的可用的內存(一個連續完整的地址空間),而實際上,它一般是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在須要時進行數據交換。)
(4)因爲程序愈來愈龐大何設計的多任務性,物理內存沒法知足要求,出現了虛擬內存,虛擬內存使得多個進程能夠共享物理內存,而且邏輯上獨立。虛擬內存提升了內存利用率,而且能夠擴展內存空間,使得一個虛擬的地址能夠映射到物理內存,文件或者其餘能夠尋址的存儲上。如一個進程在不活動的狀況下,操做系統將這個物理內存中的數據移到一個磁盤文件下(頻繁地交換物理內存和磁盤上的數據,會致使效率低下,須要關注)。java
(1)電腦的內存地址空間將被劃分爲內核地址空間和用戶空間,程序只能使用用戶空間的內存(指程序可以申請的內存)(如windows32爲默認內核空間和用戶空間的比例是1:1,linux32爲默認的比例是1:3)
(2)內核空間主要指操做系統用於程序調度、虛擬內存或者鏈接硬件資源等的程序邏輯。程序不能訪問操做系統的空間,而且不能直接訪問硬件資源,必須經過系統提供的接口調用。(每一次系統調用都會引發內核空間和內存空間的切換,這一操做比較耗時)linux
(1).java堆:用於存儲java對象的內存區域,能夠經過Xmx(最大大小)和Xms(初始大小)來控制大小,默認空餘堆內存少於40%時就擴大到Xmx,空餘堆內存大於70%時就縮小到Xms,所以,服務器通常把xmx和xms設置成同樣,避免在GC後調節堆的大小。
(2).每一個線程建立時,JVM都會爲它建立一個運行方法棧、局部變量的堆還有操做棧。
(3).類和類加載器:類和類的加載器自己一樣須要存儲空間,存儲在永久代PermGen(屬於方法區,即java堆的永久區部分)
ps:算法
而JVM的默認類加載器都不知足該條件,因此他們加載的類都不能卸載。windows
(4)NIO:NIO使用ByteBuffer.allocateDirect()方法分配內存,能夠避免數據從內核空間到用戶空間的複製,提升效率,可是該方法直接使用的是本機內存而不是java堆內存,直接的ByteBuffer對象能夠自動清理本機緩存區,可是其只是做爲GC時的一部分執行,而GC只在Java堆被填滿或者顯示調用System.gc()來執行(也就是自動的GC只檢查Java堆是否滿,而不知道NIO操做的本機內存是否須要釋放。),以致於NIO在不少框架中是經過顯示調用System.gc()執行NIO內存的釋放的(其實2對象自己有clean方法能夠釋放,見jdk隨筆額,heap&direct)
(5)JNI:JNI使得本機代碼(如C語言)能夠調用java方法,JVM會準備空間以供運行本地方法,也會增長java運行時的本機內存佔用。緩存
JVM是按照運行時數據的存儲結構來劃份內存結構的,根據不一樣的格式存儲在不一樣的區域。運行時數據包括java程序自己的數據信息和JVM運行Java程序須要的額外數據信息,java虛擬機規範將Java運行時數據分爲6種:PC寄存器數據、Java棧、堆、方法區、本地方法區、運行時常量池。服務器
PC寄存器數據:多線程
用於保存當前執行程序的內存地址,也就是記錄某線程當前執行的方法的那一條指令,如線程的執行被中斷後就會依靠這些數據來恢復(JVM規範之定義了對java方法須要記錄指針,對本地方法則沒有規範)併發
Java棧:框架
java棧與線程相關聯,每建立一個線程就會爲該線程建立一個棧,而線程中運行的每個方法則與棧中的每個棧幀關聯起來,棧幀中包含局部變量,操做棧,方法返回值等。每個方法完成,就會彈出棧幀的元素(操做棧的棧頂元素),做爲返回值,清除這個棧幀。java棧的棧頂就是當前正在執行的活動棧,PC寄存器會指向這個方法的地址。Java棧和線程對應起來,這些數據不是線程共享的,不存在一致性問題jvm
堆:
存儲Java對象的地方,因爲時全部線程共享的,因此須要關心數據的一致性問題。
方法區:
用於存儲類結構信息,如常量池、域,方法數據、方法體,構造函數、包括類中的專用方法、實力初始化、接口初始化等
運行時常量池:
包括編譯器的數字常量,方法或者域的引用。(注意,這一區域屬於方法區)
本地方法棧:
JVM爲運行native方法準備的空間。因爲不少native方法是用c語言實現的,因此又叫C棧。這個區域jvm並無嚴格的限制,由不一樣的JVM實現者自由實現。
JVM的內存分配主要基於堆和棧,
棧:
堆
靜態內存的分配和回收:類中的局部變量和對象的引用都是靜態內存分配的(這一部份內存空間在棧上分配),在編譯時這一部分空間已經肯定,只是在程序被加載時一次性分配,而當方法運行結束時隨着對應棧幀的撤銷回收。
動態內存分配和回收:像實例等數據只有在JVM解析類對象後才能知道具體須要分配多少空間,而且堆中的這些數據只有在對象再也不被引用時纔會被回收。只要某個對象再也不被其餘活動對象所引用就能夠被回收,而活動對象是指能夠被根集合對象所到達的對象。根集合對象所包含的對象跟jvm具體實現有關,可是大都會包含以下一些元素:方法中局部變量的引用、java操做棧中的對象引用、常量池中的對象引用、本地方法中持有的對象引用、類的class對象(當該Class對象再也不被使用時一樣會被回收)。
基於分代的垃圾收集算法:分爲young、old、perm三個區
young區分爲eden區和兩個survivor區,eden區滿後會觸發minorGC,minorGC後仍存活的對象將放到survivor區(若另外一個survivor區存在活動對象將放到同一個區中,保證一個survivor區是空的)
old區中已滿將會觸發FullGC,old區中存放的是:
perm區主要存放類的class對象,只有在FullGC時纔會被回收
三類垃圾收集算法,Serial Collector、Parallel Colllector、CMS Collector
JVM在client模式下的默認的GC方式(能夠經過配置jvm參數 -XX:+UserSerialGC來配置實用該算法)-XX:+PrintGCDetails 能夠配置打印GC日誌。全部建立的對象都將在Eden區分配,若是建立的對象超過Eden區的大小或者超過PretenureSizeThreshold配置(-XX:PretenureSizeThreshold=123)參數的大小都只能在old區分配。當Eden區空間不足時會觸發minorGC,可是觸發minorGC之間會檢查晉升到Old區的平均對象大小是否大於old的剩餘空間,若是大於則觸發FullGC,若是小於則根據HandlePromotionFailure(是否容許擔保失敗)參數,若是爲true則僅觸發MinorGC,不然觸發FullGC。MinorGC時除了將Eden區的非活動對象回收外,還會把一些年老的對象晉升到Old區,而這個年老對象的‘歲數’則經過 -XX:MaxTenuringThreshold=10設置(在survivor的from/to區之間移動一次則爲一歲),另外若是To的Servivor區空間不足移入對象時,這些對象也會直接放入Old區。若是old區或者Perm區空間不足時就會觸發FullGC。GC時由於是串行的,因此動做是單線程完成的,JVM中的其餘應用程序會所有中止。
Parallel GC根據MinorGC和FullGC的不一樣分爲三種,分別是ParNewGC、ParallelGC和ParallelOldGC。
ParNewGC:
能夠經過參數 -XX:+UseParNewGC參數來指定,與Serail Collector類似,只是回收是多線程並行的,而且經過一個UseAdaptiveSizePolicy配置參數來控制對象通過多少次回收後能夠直接放入old區。
ParallelGC:
是server模式JVM下的默認GC方式,能夠經過 -XX:+UserParallelGC參數來強制指定,並行回收的線程數能夠經過 -XX:ParallelGCThreads來指定,這個值有個計算公式,若是cpu核數小於8,則能夠和核數同樣,若是大於8值爲:3+(核數*5)/8,能夠經過 -Xmn:10m來控制Young區的大小,而Eden、FROM區的大小比例能夠經過 -XX:SurvivorRatio=8來設置Eden和FromSpace的比值是8:1(固然To區也佔1)。當在Eden區中申請內存空間時,若是Eden區不夠,則比較當前申請空間時否大於Eden的一半,是的話則直接在old中分配,不是的話則會執行MinorGC,可是執行MinorGC以前會檢查old區的平均晉升大小是否大於剩餘空間,大於則觸發FullGC,而且在執行FullGC後會再一次檢查old的晉升的平均大小是否大於剩餘空間,不是的話會再次觸發FullGC,也就是說可能會觸發兩次FullGC。,Young區的晉升規則能夠經過如下參數設置:AlwaysTenure:默認爲false,爲true則表示只要在MinorGC時存活則晉升,NeverTenure,默認爲false,是true則永不晉升。若是上面兩個參數都沒有配置的狀況下設置UseAdaptiveSizePolicy,則啓動時將以InitialTenuringThreshold值做爲存活次數的閥值,而且在每次GC後調整。若是不使用UseAdaptiveSizePolicy則將以MaxTenuringThreshold爲準(經過-XX:-UseAdaptiveSizePolicy設置)另外若是MinorGC時Servivor的To區空間不夠,也會直接放到old區。old或者Perm區滿時會觸發FullGC,若是配置了參數ScavengeBeforeFullGC則在FullGC以前會觸發MinorGC
PrarllelOldGC:
能夠經過 -XX:+UseParallelOldGC參數來強制指定,一樣能夠經過-XX:ParallelGCThreads來指定線程數,這個值有個計算公式,若是cpu核數小於8,則能夠和核數同樣,若是大於8值爲: 3+(核數*5)/8。與ParallelGC的不一樣在於FullGC,它的FullGC動做爲清空整個Heap對中的垃圾對象,清楚Perm區中已經被卸載的類信息,並進行壓縮,而ParallelGC只清楚部分heap堆中的垃圾對象,並對部分空間進行壓縮。
能夠經過 -XX:+UseConcMarkSweepGC來指定,併發的默認線程爲4,也能夠經過ParallelCMSThreads指定。CMS Collector使用CMS GC、Minor GC、FullGC。而CMS GC不一樣於其餘兩種GC,觸發規律是基於Old區、和Perm區的使用率(觸發後回收對應old或perm區的內存),達到必定比例就會觸發(默認是92%),該比例能夠經過CMSInitiatingOccupancyFraction來指定,另外設置讓Perm區也使用CMS GC能夠經過參數 -XX:+CMSClassUnloadingEnabled來指定。這個模式下的minorGC與Serial Collector基本一致,只是採用多線程。FullGC只在兩種狀況觸發,一種是Eden分配失敗後分配到To區,To區滿分配到Old區,Old區不夠則觸發FullGC,另一種是當CMS GC向Old申請內存失敗時會觸發FullGC。Hotspot1.6下使用這種算法並顯示調用System.gc(如Nio可能須要顯示調用),且設置了ExplicitFCInvokesConcurrent參數,將會致使內存泄露。
日誌格式:
[GC [<Collector>:<starting occupancy1> -> <ending occupancy1> (total size1) , <paise time1> secs ] <starting occupancy2> -> <ending occupancy2> (total size2), <paise time2> secs ]
<Collector> 收集器的名稱
<starting occupancy1>Young區GC前內存
<endingoccupancy1>Young區GC後內存
<paise time1>YOUNG區局部收集時JVM的暫停時間
<starting occupancy2>表示JVMHeap GC前內存
<endingoccupancy2>表示JVMHeap GC後內存
<paise time2>GC過程當中JVM的暫停總時間
GC日誌對內存泄露的判斷:
JVM Crash日誌分析: