雖然Java屏蔽了一下內存細節,但是有時候,了解一下這些常識還是有好處的,特別是一些面試,總是盯着這些玩意不放手。
JVM啟動以后,會分配兩類內存區域,一類用於開發人員使用,比如保存一些變量,對象等,一類JVM自己使用,比如存放一些class類和描述。
1,第一類內存區域又可以分為棧(stack)、堆(heap),還有一些靜態存儲區域,這部分的內存在JVM啟動的時候,可以用參數進行配置:
-Xms 初始堆大小,這個值不能太小,其初始空間(即-Xms)是物理內存的1/64,這個值不能太小,比如 設置了-Xms1m,運行可能會出現
- Error occurred during initialization of VM
- Too small initial heap for new size specified
Error occurred during initialization of VM Too small initial heap for new size specified
-Xmx 堆大小上限,最大空間(-Xmx)是物理內存的1/4,如果程序中分配的內存超過了這個限制,那么會出現
- Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
代碼為:
- byte[] b = new byte[100000000];
byte[] b = new byte[100000000];
-Xss 線程棧大小,一般不用設置,JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需內存大小進行調整。有時候會發現一下異常,
- Exception in thread "main" java.lang.StackOverflowError
Exception in thread "main" java.lang.StackOverflowError
原因一般是:
- public static int callMyself(){
- return callMyself();
- }
public static int callMyself(){ return callMyself(); }
方法的遞歸或者死循環,導致棧空間不夠用了。
棧和堆到底存些什么,很多地方都有講到,這里參考下《Think in java》的,棧里存放對象引用、基本類型的變量等,而堆里面存放對象和數組。方法的執行是在棧上進行的,這一點可以通過異常的時候,經常會默認打印
- e.printStackTrace();
e.printStackTrace();
棧信息得知。
Runtime類有幾個函數,我們可以簡單的通過這幾個函數,看看JVM中的一些內存信息。
maxMemory()這個方法返回的是java虛擬機(這個進程)能構從操作系統那里挖到的最大的內存,以字節為單位,如果在運行java程序的時 候,沒有添加-Xmx參數,那么就是64兆,也就是說maxMemory()返回的大約是64*1024*1024字節,這是java虛擬機默認情況下能 從操作系統那里挖到的最大的內存。如果添加了-Xmx參數,將以這個參數后面的值為准,例如java -cp ClassPath -Xmx512m ClassName,那么最大內存就是512*1024*0124字節。
totalMemory()這個方法返回的是java虛擬機現在已經從操作系統那里挖過來的內存大小,也就是java虛擬機這個進程當時所占用的所有 內存。如果在運行java的時候沒有添加-Xms參數,那么,在java程序運行的過程的,內存總是慢慢的從操作系統那里挖的,基本上是用多少挖多少,直 挖到maxMemory()為止,所以totalMemory()是慢慢增大的。如果用了-Xms參數,程序在啟動的時候就會無條件的從操作系統中挖- Xms后面定義的內存數,然后在這些內存用的差不多的時候,再去挖。
freeMemory()是什么呢,剛才講到如果在運行java的時候沒有添加-Xms參數,那么,在java程序運行的過程的,內存總是慢慢的從操 作系統那里挖的,基本上是用多少挖多少,但是java虛擬機100%的情況下是會稍微多挖一點的,這些挖過來而又沒有用上的內存,實際上就是 freeMemory(),所以freeMemory()的值一般情況下都是很小的,但是如果你在運行java程序的時候使用了-Xms,這個時候因為程 序在啟動的時候就會無條件的從操作系統中挖-Xms后面定義的內存數,這個時候,挖過來的內存可能大部分沒用上,所以這個時候freeMemory()可 能會有些大。
下面我們來看看例子:
- Runtime rt = Runtime.getRuntime();
- info("Max memory: " + rt.maxMemory());
- long fisrt = rt.freeMemory();
- info("Total memory: " + rt.totalMemory());
- info("Free memory: " + fisrt);
- int size = 10000;
- byte[] b = new byte[size];
- long bL = rt.freeMemory();
- info("Free memory: " + bL);
- info("byte allocate Cost memory: " + (fisrt - bL) + ", Array size :" + size);
Runtime rt = Runtime.getRuntime(); info("Max memory: " + rt.maxMemory()); long fisrt = rt.freeMemory(); info("Total memory: " + rt.totalMemory()); info("Free memory: " + fisrt); int size = 10000; byte[] b = new byte[size]; long bL = rt.freeMemory(); info("Free memory: " + bL); info("byte allocate Cost memory: " + (fisrt - bL) + ", Array size :" + size);
運行參數為 -Xms8m -Xmx32m (太大了可能看不出來),運行結果為:
- 2011-02-22 10:28:01: Max memory: 33357824
- 2011-02-22 10:28:01: Total memory: 8323072
- 2011-02-22 10:28:01: Free memory: 7791752
- 2011-02-22 10:28:01: Free memory: 7781736
- 2011-02-22 10:28:01: byte allocate Cost memory: 10016, Array size :10000
2011-02-22 10:28:01: Max memory: 33357824 2011-02-22 10:28:01: Total memory: 8323072 2011-02-22 10:28:01: Free memory: 7791752 2011-02-22 10:28:01: Free memory: 7781736 2011-02-22 10:28:01: byte allocate Cost memory: 10016, Array size :10000
33357824 <> 32*1025*1024(大約等於)
8323072 <> 8×1024×1024
最后看看10000長度的byte數組,分配了多少內存,大約為10016,這說明除了10000個大小為1字節的byte以外,還有16個字節其他的玩意。
將byte換成int(4字節):
- 2011-02-22 10:35:21: int allocate Cost memory: 40016, Array size :10000
2011-02-22 10:35:21: int allocate Cost memory: 40016, Array size :10000
與byte相同,也是4*10000+16
將byte換成long(8字節):
- 2011-02-22 10:32:47: long allocate Cost memory: 80016, Array size :10000
2011-02-22 10:32:47: long allocate Cost memory: 80016, Array size :10000
與byte相同,也是8*10000+16
再看看String數組:
- 2011-02-22 10:34:40: String allocate Cost memory: 40016, Array size :10000
2011-02-22 10:34:40: String allocate Cost memory: 40016, Array size :10000
String作為一個對象,分配的內存大小與int相同,說明了這台機器是32(4*8)位的
最后看看Object對象,
- 2011-02-22 10:37:02: Object allocate Cost memory: 40016, Array size :10000
2011-02-22 10:37:02: Object allocate Cost memory: 40016, Array size :10000
與String一樣。
2,第二類內存,我了解的主要是PermGen space,全稱是Permanent Generation space,是指內存的永久保存區域,這塊內存主要是被JVM存放Class和Meta信息的,Class在被Loader時就會被放到PermGen space中,它和存放類實例(Instance)的Heap區域不同,SUN的JDK在GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理,所以如果你的應用中有很CLASS的話,就很可能出現PermGen space錯誤。
原來SUN 的JVM把內存分了不同的區,其中一個就是permenter區用來存放用得非常多的類和類描述。本來SUN設計的時候認為這個區域在JVM啟動的時候就 固定了,但他沒有想到現在動態會用得這么廣泛。而且這個區域有特殊的垃圾收回機制,現在的問題是動態加載類到這個區域后,gc根本沒辦法回收