JAVA的堆疊和記憶體、垃圾回收解說

y_keven發表於2014-01-21

1.有關java健壯性特點的真相

很多書上都說java健壯性的特點是因為java使用陣列代替了c++的指標;c++最令人頭痛的問題就是記憶體問題,java的健壯性使程式設計人員不用再考慮記憶體的問題;這種觀點和想法是不對,java雖然有垃圾回收機制,不需要程式設計師去delete記憶體,但是在編碼的時候還是應該考慮到記憶體的,儘量不要去浪費jvm的記憶體;這裡就涉及到了java的堆疊問題。

2.java堆疊的區別

  堆:存放類型別,用new建立,垃圾自動回收機制負責回收,速度慢

1).堆是一個“執行時”資料區,類例項化物件是從堆上去分配空間的,也就是說堆上空間都是通過new指令建立的;

2).與c++的區別就是java不需要顯示的去釋放堆,而是由垃圾回收機制負責,這是因為堆是動態分配記憶體大小的,即程式執行的時候分配;

3).這也產生了一個問題,堆空間的資料記憶體讀取比較慢。

    例:String str =new String(“yangkai”);

  棧:存放基本資料型別,速度快

1).棧主要存放基本資料型別(byte、short、int、long、float、double、boolean、char)和物件控制程式碼;

2).資料可以共享;何為共享?這裡說的共享不是程式設計師決定的而是由jvm來控制,指系統自動處理的方式,比如說:int a = 10;int b= 10;這裡的a、b變數指向的棧上空間地址是同一個,這就是所謂的“資料共享”;jvm的處理邏輯:java虛擬機器去處理int a = 10的時候先去棧上建立一個變數作為a的引用,然後去查棧去找有沒有10的值,如果有就將a指向10,如果沒有就把10存進來。

3).相對於堆來說速度快;

4).棧資料的大小與生存期是確定的,缺乏靈活性。

例:int a = 0;

3.例項化物件的方法

  比如說String這個類的例項可以由兩種方法建立:

String str = new String(“yangkai”);

String str = “yangkai”;

   分析:這兩個方式建立的額物件是不同的,依照我們們上面介紹的堆疊知識應該對這種String的例項有所瞭解了吧!第一種採用new方式的在堆上開闢空間沒呼叫一次就會建立一個物件;第二種是現在棧上建立String物件的引用,然後儲存“yangkai”再讓str引用;下次再呼叫str,如果棧上有就不會再次建立了;所以一般比較String變數的時候都採用兩種方式,equals()和“==”;一般“==”判斷的是物件是否相等即是否是同一個物件;判斷出來兩個String物件相等了說明這個字串是存放在棧的,資料是共享的,這兩個字串是同一個物件;而equals()判斷兩個物件的內容是否相等,比如判斷堆上的字串的內容只能通過equals()方法來判斷,“==”判斷的可能是false,因為每一個string在堆上都是以一個String物件儲存的。

4.如何控制記憶體,這裡教你檢視jvm記憶體的使用

當前虛擬機器的最大記憶體: Runtime.getRuntime().maxMemory();

迴圈前虛擬機器已佔記憶體: Runtime.getRuntime().totalMemory();

   下面使用兩個案例來監測jvm記憶體:

package com.hudong.memory;
/**
 *
 * @Title: MemoryTest.java
 * @Copyright: Copyright (c)2005
 * @Description: <br>
 * <br>
 *              測試java虛擬機器的記憶體佔用情況
* @Created on 2014-1-21 上午11:40:35
 * @author楊凱
 */
public class MemoryTest {
 
    /**
    * @param args
    */
    public static void main(String[]args) {
       testStringBuffer();
       // testString();
    }
 
    /**
    * 測試String例項物件的佔用的記憶體
    */
    public static void testString() {
       String str = new String("abcdefghijklmn");
       int count = 0;
       System.out.println("當前虛擬機器的最大記憶體:" + Runtime.getRuntime().maxMemory()/ 1024 / 1024 + "m------" + Runtime.getRuntime().maxMemory()+ "byte");
       System.out.println("迴圈前虛擬機器已佔記憶體:" + Runtime.getRuntime().totalMemory()/ 1024 / 1024 + "m====" + Runtime.getRuntime().totalMemory()+ "byte");
       while (true) {
           try {
                str += str;
                count++;
            } catch (Error e) {
                System.out.println("迴圈次數:" + count);
                System.out.println("字串迴圈後的大小為:" + str.length() / 1024 / 1024 + "m-=-=-=" + str.length() + "byte");
                System.out.println("迴圈前虛擬機器已佔記憶體:" + Runtime.getRuntime().totalMemory()/ 1024 / 1024 + "m====" + Runtime.getRuntime().totalMemory()+ "byte");
                System.out.println("遇到錯誤:" + e);
                break;
           }
       }
       /*
        * 執行結果:
        * 當前虛擬機器的最大記憶體:793m------832438272byte
           迴圈前虛擬機器已佔記憶體:127m====133234688byte
           迴圈次數:23
           字串迴圈後的大小為:112m-=-=-=117440512byte
           迴圈前虛擬機器已佔記憶體:642m====673669120byte
           遇到錯誤:java.lang.OutOfMemoryError: Java heap space
        */
    }
 
    /**
     * 測試StringBuffer例項物件的佔用的記憶體
    */
    public static void testStringBuffer(){
       StringBuffer sb = new StringBuffer("abcdefghijklmn");
       int count = 0;
       System.out.println("當前虛擬機器的最大記憶體:" + Runtime.getRuntime().maxMemory()/ 1024 / 1024 + "m------" + Runtime.getRuntime().maxMemory()+ "byte");
       System.out.println("迴圈前虛擬機器已佔記憶體:" + Runtime.getRuntime().totalMemory()/ 1024 / 1024 + "m====" + Runtime.getRuntime().totalMemory()+ "byte");
       while (true) {
           try {
                sb.append(sb);
                count++;
           } catch (Error e) {
                System.out.println("迴圈次數:" + count);
                System.out.println("字串迴圈後的大小為:" + sb.length() / 1024 / 1024 + "m-=-=-=" + sb.length() + "byte");
                System.out.println("迴圈前虛擬機器已佔記憶體:" + Runtime.getRuntime().totalMemory()/ 1024 / 1024 + "m====" + Runtime.getRuntime().totalMemory()+ "byte");
                System.out.println("遇到錯誤:" + e);
                break;
           }
       }
       /*
        * 執行結果:
        * 當前虛擬機器的最大記憶體:793m------832438272byte
           迴圈前虛擬機器已佔記憶體:127m====133234688byte
           迴圈次數:23
           字串迴圈後的大小為:112m-=-=-=117440512byte
           迴圈前虛擬機器已佔記憶體:539m====566108160byte
           遇到錯誤:java.lang.OutOfMemoryError: Java heap space
 
        */
    }
}


   分析:以上兩個程式用來說明String和StringBuffer兩個佔用記憶體的情況;其他資料上面介紹這兩個類的使用的時候得出的結果是:Stringbuffer比String迴圈後得到的字串位元組大;而我在這測試的時候得到結果兩個類得到的字串大小是一樣的,區別是耗費的額虛擬機器的記憶體大小不一樣,後者明顯的比前者小。

  注:安裝完jdk之後jvm預設的大小是63m,上面我測試的時候是將jvm的配置檔案的xmx大小調大之後的。

相關文章