操做系統: 1,32位機器和64位機器 1、32位和64位是什麼意思 一、32位和64位表示CPU一次能處理的最大位數; 二、32位CPU只能安裝32位系統,64位CPU能夠安裝32位和64位系統; 三、現在市面上的CPU大多數爲64位,怎麼看CPU是32位仍是64位。 2、尋址能力 一、32位系統的最大尋址空間是2的32次方=4294967296(bit)= 4(GB)左右,也就是說在32位機器整個操做系統中,全部應用程序加起來最多隻能使用4g內存。 二、64位系統的最大尋址空間爲2的64次方=4294967296(bit)的32次方,數值大於1億GB; 三、也就是說32位系統的處理器最大隻支持到4G內存,而64位系統最大支持的內存高達億位數,實際使用過程當中大多數的電腦32位系統最多識別3.5GB內存,64位系統最多識別128GB內存。html
那麼32位系統如何實現所謂超過4G內存破解,原理很簡單,如今CPU基本都是64位處理器,也就是硬件是沒有4GB的識別問題,破解就是應用64位系統尋址原理,來打開32位系統對超過4G內存識別限制。而將這些多出來內存則做爲RAMDISK來使用,就是緩存盤,在WINDOWS 下軟件運行都會產生臨時文件,那麼他就是將這些軟件產品臨時文件都搬到RANDISK上來,而不寫入磁盤。在某個角度上來的確能夠提升系統運行速度。但並非真正系統和軟件使用64位系統和軟件尋址方式,仍然是32位的尋址。若是將這個破解應用在純32位CPU上,你再試試看他可否能打開所謂超4G內存的破解。java
數據在存儲器(RAM)中存放是有規律的 ,CPU在運算的時候須要把數據提取出來就須要知道數據存放在哪裏 ,這時候就須要挨家挨戶的找,這就叫作尋址,但若是地址太多超出了CPU的能力範圍,CPU就沒法找到數據了。 CPU最大能查找多大範圍的地址叫作尋址能力 ,CPU的尋址能力以字節爲單位,如32位尋址的CPU能夠尋址2的32次方大小的地址也就是4G,這也是爲何32位的CPU最大能搭配4G內存的緣由,再多的話CPU就找不到了。linux
一般人們認爲,內存容量越大,處理數據的能力也就越強,但內存容量不可能無限的大,它要受到系統結構、硬件設計、制形成本等多方面因素的制約,一個最直接的因素取決於系統的地址總線的地址寄存器的寬度(位數)。 計算機的尋找範圍由總線寬度(處理器的地址總線的位數)決定的,也能夠理解爲cpu寄存器位數,這兩者通常是匹配的。web
Intel公司早期的CPU產品的地址總線和地址寄存器的寬度爲20位,即CPU的尋址能力爲2^20=10241024字節=1024K字節=1M字節;286的地址總線和地址寄存器的寬度爲24位,CPU的尋址能力爲2^24=1024410244B=410244KB=16M;386及386以上的地址總線和地址寄存器的寬度爲32位,CPU的尋址能力爲2^32=4096M字節=4G字節。 也就是說,若是機器的CPU過早,即便有很大的內存也不能獲得利用,而對於如今的PⅡ級的CPU,其尋址能力已遠遠超過目前的內存容量。由此推出:地址總線爲N位(N一般都是8的整數倍;也說N根數據總線)的CPU尋址範圍是2的N次方字節,即2^N(B)算法
若是真正想有效讓系統分配超過4G的內存,建議仍是使用64位系統,只有64位系統才能原生支持超過4G內存的尋址。api
參考:blog.csdn.net/fengyuwuzu0…數組
一個英文字符不一樣編碼使用的字節數: UTF-8編碼長度:1 GBK編碼長度:1 GB2312編碼長度:1緩存
1KB=1024B 1MB=1024KB 1GB=1024MB安全
對於2億條評論內容佔的空間大小,評價一條評價50字左右,一個漢字3個字節,2億條評論,1GB=102410241024,須要的空間大於3gb服務器
3,Java對象的表示模型和運行時內存表示
<1>Hotspot主要是用C++寫的,因此它定義的Java對象表示模型也是基於C++實現的。 Java對象的表示模型叫作「OOP-Klass」二分模型,包括兩部分:
4,Java對象大小、對象內存佈局及鎖狀態變化 Mark Word:存儲對象運行時記錄信息,佔用內存大小與機器位數同樣,即32位機佔4字節,64位機佔8字節 元數據指針:指向描述類型的Klass對象(Java類的C++對等體)的指針,Klass對象包含了實例對象所屬類型的元數據,所以該字段被稱爲元數據指針,JVM在運行時將頻繁使用這個指針定位到位於方法區內的類型信息。這個數據的大小稍後說 數組長度:數組對象特有,一個指向int型的引用類型,用於描述數組長度,這個數據的大小和元數據指針大小相同,一樣稍後說 實例數據:實例數據就是8大基本數據類型byte、short、int、long、float、double、char、boolean(對象類型也是由這8大基本數據類型複合而成),每種數據類型佔多少字節就不一一例舉了 填充:補齊位置使得總體爲8字節的整數倍,HotSpot的對齊方式爲8字節對齊,即一個對象必須爲8字節的整數倍,所以若是最後前面的數據大小爲17則填充7,前面的數據大小爲18則填充6,以此類推
說說元數據指針的大小。元數據指針是一個引用類型,所以正常來講64位機元數據指針應當爲8字節,32位機元數據指針應當爲4字節,可是HotSpot中有一項優化是對元數據類型指針進行壓縮存儲,使用JVM參數: -XX:+UseCompressedOops開啓壓縮 -XX:-UseCompressedOops關閉壓縮 HotSpot默認是前者,即開啓元數據指針壓縮,當開啓壓縮的時候,64位機上的元數據指針將佔據4個字節的大小。換句話說就是當開啓壓縮的時候,64位機上的引用將佔據4個字節,不然是正常的8字節。
舉例指針壓縮 首先是Object對象的大小: 開啓指針壓縮時,8字節Mark Word + 4字節元數據指針 = 12字節,因爲12字節不是8的倍數,所以填充4字節,對象Object佔據16字節內存 關閉指針壓縮時,8字節Mark Word + 8字節元數據指針 = 16字節,因爲16字節正好是8的倍數,所以不須要填充字節,對象Object佔據16字節內存
接着是字符'a'的大小: 開啓指針壓縮時,8字節Mark Word + 4字節元數據指針 + 1字節char = 13字節,因爲13字節不是8的倍數,所以填充3字節,字符'a'佔據16字節內存 關閉指針壓縮時,8字節Mark Word + 8字節元數據指針 + 1字節char = 17字節,因爲17字節不是8的倍數,所以填充7字節,字符'a'佔據24字節內存
接着是整型1的大小: 開啓指針壓縮時,8字節Mark Word + 4字節元數據指針 + 4字節int = 16字節,因爲16字節正好是8的倍數,所以不須要填充字節,整型1佔據16字節內存 關閉指針壓縮時,8字節Mark Word + 8字節元數據指針 + 4字節int = 20字節,因爲20字節正好是8的倍數,所以填充4字節,整型1佔據24字節內存
接着是字符串"aaaaa"的大小,全部靜態字段不須要管,只關注實例字段,String對象中實例字段有"char value[]"與"int hash",由此可知: 開啓指針壓縮時,8字節Mark Word + 4字節元數據指針 + 4字節引用 + 4字節int = 20字節,因爲20字節不是8的倍數,所以填充4字節,字符串"aaaaa"佔據24字節內存 關閉指針壓縮時,8字節Mark Word + 8字節元數據指針 + 8字節引用 + 4字節int = 28字節,因爲28字節不是8的倍數,所以填充4字節,字符串"aaaaa"佔據32字節內存
最後是長度爲1的char型數組的大小: 開啓指針壓縮時,8字節的Mark Word + 4字節的元數據指針 + 4字節的數組大小引用 + 1字節char = 17字節,因爲17字節不是8的倍數,所以填充7字節,長度爲1的char型數組佔據24字節內存 關閉指針壓縮時,8字節的Mark Word + 8字節的元數據指針 + 8字節的數組大小引用 + 1字節char = 25字節,因爲25字節不是8的倍數,所以填充7字節,長度爲1的char型數組佔據32字節內存
備註:開啓指針壓縮技術的前提條件:當jvm是64位且內存小於32G會默認開啓指針壓縮技術,那麼按理說,4字節的最大尋址空間應該只有2的32次方,即4GB內存堆內存,明顯不符合使用了,實際上不是這樣的, 解釋:並不是如此, 因爲對象是8字節對齊的, 所以對象起始地址最低三位老是0, 所以能夠存儲時能夠右移3bit, 高位空出來的3bit能夠表示更高的數值, 實際上, 可使用指針壓縮的maxHeapSize是4G * 8 = 32G.
4,java如何計算一個對象的大小。 blog.csdn.net/bobpauline/… www.jianshu.com/p/9d729c9c9… blog.csdn.net/iter_zc/art…
public class ObjectSizeServiceTest { public static class Person { //private String name = "zhangsan"; private int age = 10; // private long age2 = 100; private double age3 = 20.3;
}
public static void main(String[] args) {
//藉助lucene的RamUsageEstimator,內部是基於Unsafe實現,實現了遞歸處理獲取引用對象的大小
Object object = new Object();
Integer integerValue = 100;
//System.out.println(RamUsageEstimator.sizeOf(integerValue));
//System.out.println(RamUsageEstimator.sizeOf(object));
Person person = new Person();
System.out.println(RamUsageEstimator.sizeOf(person));
System.out.println(getSizeByUnsafe(person));
}
/**
* 對象頭部的大小
*/
private static final int OBJECT_HEADER_SIZE = 8;
/**
* 對象佔用內存的最小值
*/
private static final int MINIMUM_OBJECT_SIZE = 8;
/**
* 對象按多少字節的粒度進行對齊
*/
private static final int OBJECT_ALIGNMENT = 8;
// 得到Unsafe實例
//暫時不考慮對象的相互引用,還有超類,字符串,數組
public static long getSizeByUnsafe(Object object) {
Unsafe unsafe;
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(null);
} catch (Throwable t) {
unsafe = null;
}
Class kClass = object.getClass();
long lastFieldOffset = -1;
for (Field f : kClass.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
lastFieldOffset = Math.max(lastFieldOffset, unsafe.objectFieldOffset(f));
}
}
if (lastFieldOffset > 0) {
lastFieldOffset += 1;
if ((lastFieldOffset % OBJECT_ALIGNMENT) != 0) {
lastFieldOffset += OBJECT_ALIGNMENT - (lastFieldOffset % OBJECT_ALIGNMENT);
}
return Math.max(MINIMUM_OBJECT_SIZE, lastFieldOffset);
}
//該對象沒有任何屬性
long size = OBJECT_HEADER_SIZE;
if ((size % OBJECT_ALIGNMENT) != 0) {
size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);
}
return Math.max(MINIMUM_OBJECT_SIZE, size);
}
複製代碼
} 5,垃圾回收器 從線程運行狀況分類有三種 串行回收,Serial回收器,單線程回收,全程stw; 並行回收,名稱以Parallel開頭的回收器,多線程回收,全程stw; 併發回收,cms與G1,多線程分階段回收,只有某階段會stw;
6,jvm內存分區 JVM的內存劃分中,有部分區域是線程私有的,有部分是屬於整個JVM進程;有些區域會拋出OOM異常,有些則不會,瞭解JVM的內存區域劃分以及特徵,是定位線上內存問題的基礎。那麼JVM內存區域是怎麼劃分的呢?
第一,程序計數器(Program Counter Register),在JVM規範中,每一個線程都有本身的程序計數器(因此說這塊空間是線程安全的)。這是一塊比較小的內存空間,存儲當前線程正在執行的Java方法的JVM指令地址,即字節碼的行號。若是正在執行Native方法,則這個計數器爲空。該內存區域是惟一一個在Java虛擬機規範中沒有規定任何OOM狀況的內存區域。(程序計數器是一個記錄着當前線程所執行的字節碼的行號指示器,JAVA代碼編譯後的字節碼在未通過JIT(實時編譯器)編譯前,其執行方式是經過「字節碼解釋器」進行解釋執行。簡單的工做原理爲解釋器讀取裝載入內存的字節碼,按照順序讀取字節碼指令。讀取一個指令後,將該指令「翻譯」成固定的操做,並根據這些操做進行分支、循環、跳轉等流程。 )
第二,Java虛擬機棧(Java Virtal Machine Stack),一樣也是屬於線程私有區域,每一個線程在建立的時候都會建立一個虛擬機棧,生命週期與線程一致,線程退出時,線程的虛擬機棧也回收。虛擬機棧內部保持一個個的棧幀,每次方法調用都會進行壓棧,JVM對棧幀的操做只有出棧和壓棧兩種,方法調用結束時會進行出棧操做。
該區域存儲着局部變量表,編譯時期可知的各類基本類型數據、對象引用、方法出口等信息。
第三,本地方法棧(Native Method Stack)與虛擬機棧相似,本地方法棧是在調用本地方法時使用的棧,每一個線程都有一個本地方法棧。
第四,堆(Heap),幾乎全部建立的Java對象實例,都是被直接分配到堆上的。堆被全部的線程所共享,在堆上的區域,會被垃圾回收器作進一步劃分,例如新生代、老年代的劃分。Java虛擬機在啓動的時候,可使用「Xmx」之類的參數指定堆區域的大小。
第五,方法區(Method Area)。方法區與堆同樣,也是全部的線程所共享,存儲被虛擬機加載的元(Meta)數據,包括類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。這裏須要注意的是運行時常量池也在方法區中。根據Java虛擬機規範的規定,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。因爲早期HotSpot JVM的實現,將CG分代收集拓展到了方法區,所以不少人會將方法區稱爲永久代。Oracle JDK8中已永久代移除永久代,同時增長了元數據區(Metaspace)。
第六,運行時常量池(Run-Time Constant Pool),這是方法區的一部分,受到方法區內存的限制,當常量池沒法再申請到內存時,會拋出OutOfMemoryError異常。 在Class文件中,除了有類的版本、方法、字段、接口等描述信息外,還有一項信息是常量池。每一個Class文件的頭四個字節稱爲Magic Number,它的做用是肯定這是不是一個能夠被虛擬機接受的文件;接着的四個字節存儲的是Class文件的版本號。緊挨着版本號以後的,就是常量池入口了。常量池主要存放兩大類常量:
字面量(Literal),如文本字符串、final常量值 符號引用,存放了與編譯相關的一些常量,由於Java不像C++那樣有鏈接的過程,所以字段方法這些符號引用在運行期就須要進行轉換,以便獲得真正的內存入口地址。 class文件中的常量池,也稱爲靜態常量池,JVM虛擬機完成類裝載操做後,會把靜態常量池加載到內存中,存放在運行時常量池。 第七,直接內存(Direct Memory),直接內存並不屬於Java規範規定的屬於Java虛擬機運行時數據區的一部分。Java的NIO可使用Native方法直接在java堆外分配內存,使用DirectByteBuffer對象做爲這個堆外內存的引用。
OOM可能發生在哪些區域上? 根據javadoc的描述,OOM是指JVM的內存不夠用了,同時垃圾收集器也沒法提供更多的內存。從描述中能夠看出,在JVM拋出OutOfMemoryError以前,垃圾收集器通常會出馬先嚐試回收內存。 從上面分析的Java數據區來看,除了程序計數器不會發生OOM外,哪些區域會發生OOM的狀況呢?
第一,堆內存。堆內存不足是最多見的發送OOM的緣由之一,若是在堆中沒有內存完成對象實例的分配,而且堆沒法再擴展時,將拋出OutOfMemoryError異常,拋出的錯誤信息是「java.lang.OutOfMemoryError:Java heap space」。當前主流的JVM能夠經過-Xmx和-Xms來控制堆內存的大小,發生堆上OOM的多是存在內存泄露,也多是堆大小分配不合理。
第二,Java虛擬機棧和本地方法棧,這兩個區域的區別不過是虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則爲虛擬機使用到的Native方法服務,在內存分配異常上是相同的。在JVM規範中,對Java虛擬機棧規定了兩種異常:1.若是線程請求的棧大於所分配的棧大小,則拋出StackOverFlowError錯誤,好比進行了一個不會中止的遞歸調用;2. 若是虛擬機棧是能夠動態拓展的,拓展時沒法申請到足夠的內存,則拋出OutOfMemoryError錯誤。
第三,直接內存。直接內存雖然不是虛擬機運行時數據區的一部分,但既然是內存,就會受到物理內存的限制。在JDK1.4中引入的NIO使用Native函數庫在堆外內存上直接分配內存,但直接內存不足時,也會致使OOM。
第四,方法區。隨着Metaspace元數據區的引入,方法區的OOM錯誤信息也變成了「java.lang.OutOfMemoryError:Metaspace」。對於舊版本的Oracle JDK,因爲永久代的大小有限,而JVM對永久代的垃圾回收並不積極,若是往永久代不斷寫入數據,例如String.Intern()的調用,在永久代佔用太多空間致使內存不足,也會出現OOM的問題,對應的錯誤信息爲「java.lang.OutOfMemoryError:PermGen space」
內存區域 是否線程私有 是否可能發生OOM 程序計數器 是 否 虛擬機棧 是 是 本地方法棧 是 是 方法區 否 是 直接內存 否 是 堆 否 是 參考文章:www.cnblogs.com/QG-whz/p/96…
7,jvm調優 JVM中最大堆大小有三方面限制:相關操做系統的數據模型(32-bt仍是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32位系統下,通常限制在1.5G~2G;64爲操做系統對內存無限制。在Windows Server 2003 系統,3.5G物理內存,JDK5.0下測試,最大可設置爲1478m,linux系統自身通常佔用1g的內存
<1>默認的新生代 ( Young ) 與老年代 ( Old ) 的比例的值爲 1:2 ( 該值能夠經過參數 –XX:NewRatio 來指定) 新生代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小。其中,新生代 ( Young ),被細分爲 Eden 和 兩個 Survivor 區域,這兩個 Survivor 區域分別被命名爲 from 和 to,以示區分。默認,Edem : from : to = 8 :1 : 1 ( 能夠經過參數–XX:SurvivorRatio 來設定 ),即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。
<2>舉例配置: java -Xmx3550m -Xms3550m -Xmn2g –Xss128k
-Xmx3550m:設置JVM最大可用內存爲3550M。 -Xms3550m:設置JVM促使內存爲3550m。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。 -Xmn2g:設置年輕代大小爲2G。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代通常固定大小爲64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。 -Xss128k:設置每一個線程的堆棧大小。JDK5.0之後每一個線程堆棧大小爲1M,之前每一個線程堆棧大小爲256K。更具應用的線程所需內存大小進行調整。在相同物理內存下,減少這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成,經驗值在3000~5000左右。
備註: -Xss規定了每一個線程堆棧的大小。通常狀況下256K是足夠了。影響了此進程中併發線程數大小。 -Xms初始的Heap的大小。 -Xmx最大Heap的大小。 永久代 PermSize和MaxPermSize設置爲老年代存活對象的1.2-1.5倍(好比-XX:MaxPermSize=16m:設置持久代大小爲16m,這個空間比較小)。 在不少狀況下,-Xms和-Xmx設置成同樣的。這麼設置,是由於當Heap不夠用時,會發生內存抖動,影響程序運行穩定性。
HotSpot虛擬機在1.8以後已經取消了永久代,改成元空間,類的元信息被存儲在元空間中。元空間沒有使用堆內存,而是與堆不相連的本地內存區域。因此,理論上系統可使用的內存有多大,元空間就有多大,因此不會出現永久代存在時的內存溢出問題。這項改造也是有必要的,永久代的調優是很困難的,雖然能夠設置永久代的大小,可是很難肯定一個合適的大小,由於其中的影響因素不少,好比類數量的多少、常量數量的多少等。永久代中的元數據的位置也會隨着一次full GC發生移動,比較消耗虛擬機性能。同時,HotSpot虛擬機的每種類型的垃圾回收器都須要特殊處理永久代中的元數據。將元數據從永久代剝離出來,不只實現了對元空間的無縫管理,還能夠簡化Full GC以及對之後的併發隔離類元數據等方面進行優化。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0 -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲4,則年輕代與年老代所佔比值爲1:4,年輕代佔整個堆棧的1/5 -XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。設置爲4,則兩個Survivor區與一個Eden區的比值爲2:4,一個Survivor區佔整個年輕代的1/6 -XX:MaxPermSize=16m:設置持久代大小爲16m。 -XX:MaxTenuringThreshold=0:設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的概論。
<3>假設給你4g內存空間的機器,你怎麼設置堆內存 1,因爲linux系統通常佔用1g內存空間,所以系統分配1g 2,線上服務器通常是200個jvm活躍線程,每一個線程棧的空間默認爲1M,所以棧空間佔據200m。 3,永久代通常在50m能夠知足使用了。 4,程序計數器通常在20kb一下,所以這塊空間能夠忽略。 5,所以堆可用空間爲2700mb,按照新生代和老年代1:2空間設置 第五點,個人理解:新生代須要頻繁的youngc,而且還須要進行空間的壓縮,若是太大,消耗的時間太長了,老年代不須要每次都壓縮,所以在有碎片的同時,大內存能夠放入更多的數據 備註:若是有常駐內存的數據,應該使用堆外內存,堆內空間雖然變小了,致使full gc上升,可是full gc的時間會變短(所以gc掃描的空間小了)
<6>堆外內存的好處是: (1)能夠擴展至更大的內存空間。好比超過1TB甚至比主存還大的空間; (2)理論上能減小GC暫停時間,堆外內存會受gc的控制,之因此能減小gc暫停時間,若是把堆外內存的數據都放在堆內,每次gc都要全面掃描這塊空間,若是放在堆外,gc只會掃描那些沒有被引用的空間,所以常駐內存的本地緩存數據使用堆外內存更加合適。 (3)能夠在進程間共享,減小JVM間的對象複製,使得JVM的分割部署更容易實現; (4)它的持久化存儲能夠支持快速重啓,同時還可以在測試環境中重現生產數 (5)零拷貝提高數據的訪問效率
堆外內存的釋放過程: DirectByteBuffer對象在建立的時候關聯了一個PhantomReference,說到PhantomReference它其實主要是用來跟蹤對象什麼時候被回收的, 它不能影響gc決策,可是gc過程當中若是發現某個對象除了只有PhantomReference引用它以外,並無其餘的地方引用它了, 那將會把這個引用(Cleaner)放到java.lang.ref.Reference.pending隊列裏, 在gc完畢的時候通知ReferenceHandler這個守護線程去執行一些後置處理, 而DirectByteBuffer關聯的PhantomReference是PhantomReference的一個子類, 在最終的處理裏會經過Unsafe的free接口來釋放DirectByteBuffer對應的堆外內存塊
參考:www.cnblogs.com/andy-zhou/p…
8,Linux與JVM的內存關係分析 在一些物理內存爲8g的server上,主要執行一個Java服務,系統內存分配例如如下:Java服務的JVM堆大小設置爲6g,一個監控進程佔用大約600m,Linux自身使用大約800m。 Linux和Java NIO在內核內存上開闢空間給程序使用,主要是下降不要的複製,以下降IO操做系統調用的開銷。好比,將磁盤文件的數據發送網卡,使用普通方法和NIO時。數據流動比較下圖所看到的: 將數據在內核內存和用戶內存之間拷貝是比較消耗資源和時間的事情,而從上圖咱們可以看到。經過NIO的方式下降了2次內核內存和用戶內存之間的數據拷貝。這是Java NIO高性能的重要機制之中的一個(還有一個是異步非堵塞)。 從上面可以看出。內核內存對於Java程序性能也很重要,所以,在劃分系統內存使用時候。必定要給內核留出必定可用空間。 參考:www.cnblogs.com/bhlsheji/p/…
9,cms參數配置和gc日誌 blog.csdn.net/zqz_zqz/art…
10,gc調優 首先了解堆內存大致設置 最大堆設置,在單機web server的狀況下,最大堆的設置建議在物理內存的1/2到2/3之間,若是是16G的物理內存的話,最大堆的設置應該在8000M-10000M之間。 Java進程消耗的總內存確定大於最大堆設置的內存:堆內存(Xmx)+ 方法區內存(MaxPermSize,通常項目256M夠用了)+ 棧內存(Xss,包括虛擬機棧和本地方法棧,默認配置1M的棧空間)*線程數 + NIO direct memory + socket緩存區(receive37KB,send25KB)+ JNI代碼 + 虛擬機和GC自己+程序計數器(通常16kb,能夠忽略不計)*線程數 = java的內存。
線程數通常指是業務線程+netty容器線程
舉例,16GB內存機器,800MB(線程棧)+256MB(方法區)+1000MB(linux系統)+200MB(系統預留)+200MB(JNI代碼),留給堆內存可分配空間在12GB如下
進行gc調優以前應該瞭解的基本知識 <1>在進行GC優化以前,須要確認項目的架構和代碼等已經沒有優化空間。咱們不能期望一個系統架構有缺陷或者代碼層次優化沒有窮盡的應用,經過GC優化令其性能達到一個質的飛躍。 <2>經過上述分析,能夠看出虛擬機內部已有不少優化來保證應用的穩定運行,因此不要爲了調優而調優,不當的調優可能拔苗助長。 <3>最後,GC優化是一個系統而複雜的工做,沒有萬能的調優策略能夠知足全部的性能指標。GC優化必須創建在咱們深刻理解各類垃圾回收器的基礎上,纔能有事半功倍的效果。 <4>gc優化兩個核心的關注點就是:下降GC頻率,縮短GC停頓時間。 在分代GC算法中,下降回收頻率能夠經過:(1) 下降對象分配/提高率;(2) 增長代空間的大小。減小新生代大小能夠縮短新生代GC停頓時間,由於這樣被複制到survivor區域或者被提高的數據更少
從微服務架構上的一些總結 <1>進行gc調優以前應該儘可能思考原有的架構是否存在缺陷,代碼有沒有優化空間。 舉例 ①假設你的服務在2000ms,那你jvm調參能有多大空間?所以這時候的着重點是在服務內部。 ②api項目每每都涉及不少服務的調用,若是內部都是串行化的執行邏輯致使耗時較長,會致使這些對象在young區駐留的時間較長(好比一次minor gc耗時50ms,api耗時100ms,所以在minor gc時api會延長50ms,99線就會被大幅增大,在gc的時候,eden區會向survivor遷移,若是api耗時優化在50ms如下,那服務的99線就會降低不少),所以api能夠把一些邏輯併發異步化,使得api總體耗時降了下來,在下降均線的同時把99線下降。 這種場景一般指:minor gc的耗時和服務的響應時間很接近(短耗時的服務,比gc耗時稍長一點),所以gc會延長99線,使得均線和99線差距很大(好比均線5ms,99線在55ms),若是把服務響應優化在gc時間範圍內,那麼99線將會接近於均線。 ③若是項目存在大量的短生命週期對象,而且這些對象中不少數據都是冗餘的,若是對項目進行壓測,會出現大量的minor gc,甚至還出現大量major gc,服務性能壓不上去,此時的作法能夠有兩種:1、加大堆內存的eden區空間,使得新生代的gc頻率變的更低。2、優化依賴服務的架構,數據改爲按需索取,減小短生命週期對象的佔用新生代空間。 ④若是常常有內存泄漏發生,應該經過內存信息排查哪些內存沒有被正常釋放。 ⑤開啓逃逸分析,減小堆內存空間的消耗(www.jianshu.com/p/20bd2e9b1… <2>GC優化通常步驟能夠歸納爲:肯定目標、優化參數、驗收結果。 在互聯網應用裏面更多關注的是:低延遲。
優化案例1: Major GC和Minor GC頻繁(致使99線高居不下,api壓測期間,項目出現大量的young gc,old gc) api項目一般都是一些大量的短生命週期對象,所以年輕代應該更大一些。緣由:若是eden區滿了,會向survivor遷移(數據的複製成爲影響服務99線的核心因素),年輕代設置更大一些,eden對象在短週期被銷燬,不用遷移,所以99線更低。service項目一般會有一些本地緩存數據,所以老年代更加大一些,甚至可使用堆外內存代替老年代,減小full gc頻率。
優化案例2:請求高峯期發生GC,致使服務可用性降低(服務超時不少) GC日誌顯示,高峯期CMS在重標記(Remark)階段耗時1.39s。Remark階段是Stop-The-World(如下簡稱爲STW)的,即在執行垃圾回收時,Java應用程序中除了垃圾回收器線程以外其餘全部線程都被掛起,意味着在此期間,用戶正常工做的線程所有被暫停下來,這是低延時服務不能接受的。本次優化目標是下降Remark時間。
優化參考根據:cms在標記對象是否可達,其實是要重新生代開始向老年代掃描的(由於有一些對象會跨代引用),所以通常在cms在併發標記以前進行一次minor gc,可是在從新標記以前,新生代一樣會產生大量的對象,所以在從新標記時,若是再也不進行一次minor gc,會增長remark的stw時間,CMS提供CMSScavengeBeforeRemark參數,用來保證Remark前強制進行一次Minor GC。(優化案例減小200ms的延遲)
總體參考:www.jianshu.com/p/af2e21258… blog.csdn.net/zhoudaxia/a… blog.csdn.net/u022726695/… www.cnblogs.com/qmfsun/p/53… www.cnblogs.com/hirampeng/p… blog.csdn.net/u010556151/… 11,延遲(latency)和吞吐量(throghtput)
延遲和吞吐量,是衡量軟件系統的最多見的兩個指標。 延遲通常包括單向延遲(One-way Latency)和往返延遲(Round Trip Latency),實際測量時通常取往返延遲。它的單位通常是ms、s、min、h等。 吞吐量通常指至關一段時間內測量出來的系統單位時間處理的任務數或事務數(TPS)。注意「至關一段時間」,不是幾秒,而多是十幾分鍾、半個小時、一天、幾周甚至幾月。它的單位通常是TPS、每單位時間寫入磁盤的字節數等
低延遲必定意味着高吞吐量嗎?若是不是,試舉出反例。 舉例:飛機和火車在一段時間內的運貨量,飛機的速度很快,可是在較長一段時間吞吐量遠比不上火車。
12,cpu的load比較高的緣由排查 <1>首先定位是否是jvm線程消耗的cpu比較高(top命令),若是是,那就根據pid,便是進程id。 <2>查看進程的全部線程對應的cpu時間片大小(top命令)。 <3>jstack 查看指定線程id的上下文堆棧信息,根據堆棧信息去分析代碼,肯定是否是業務邏輯代碼有問題。 <4>配合查看gc log,肯定是否出現頻繁的major gc,致使cpu消耗急劇上升(如內存泄漏,致使可用堆空間降低,致使gc頻繁出發,gc時會併發使用多核cpu,致使load比較高)。
參考demo:好比多線程爬第三方數據,對方響應的數據是大對象,因爲網絡io是流傳輸的,我是一次調用完,寫到內存才進行解析的,所以young區很快寫滿了,進而晉升到old區,結果內存仍是不夠,接着繼續old gc,這時候cpu使用立馬飆升,電腦迅速發熱。