1. 程式人生 > >Java基本型別與引用型別 以及 Java記憶體知識整理

Java基本型別與引用型別 以及 Java記憶體知識整理

Java基本型別和引用型別是什麼?每種基本型別都佔多少位多少位元組?詳見下表

基本型別

整型

byte

1位元組

-27~27-1   -128~127

short

2位元組

-215~215-1  -32,768~32,767 (3萬多)

int

4位元組

-231~231-1  (10位  超過20)

long

8位元組

-263~263-1  

浮點型

float

4位元組

32IEEE 754單精度(有效位數67位)

double

8位元組

64IEEE 754雙精度(有效位數15位)

Unicode編碼的字元單元

char

2位元組

整個Unicode字符集

真值型別

boolean

1位元組

True或者false

引用型別

class

Object

Object是類層次結構的根類,每個類都使用Object作為超類,所有物件(包括陣列)都實現這個類的方法

String

String類代表字串,Java程式中的所有字串字面值(如"abc")都作為此類的例項來實現。

Date

Date表示特定的瞬間,精確到毫秒。

Void

Void 類是一個不可例項化的佔位符類,它保持一個對代表 Java關鍵字voidClass物件的引用。

對應的Class

Integer  Long  Boolean  Byte  Character  Double  Float   Short

介面interface

Runnable  Cloneable  Set  List  Map  Collection  Serializable  Comparable  Iterator

 先大致整理了一下,隨後有時間再詳細分析。

另外一些基礎知識的補充:

1. 位元組  byte       位  bit           1 byte = 8 bit  (1 Byte等於 1個 8位)

2. 基本型別,也叫內建型別,是Java中不同於類的一種特殊型別,我們在平時的開發過程中也使用最為頻繁。

    基本型別分為三類:

        字元型別    char    對應的包裝類    Character

        布林型別    boolean    對應的包裝類    Boolean

        數值型別   

byte                 Byte

short               Short

int                    Integer

long                 Long

float                 Float

double            Double

java中的數值型別不存在無符號的,它們的取值範圍是固定的,不會隨著機器硬體環境或者作業系統的改變而改變。

Java記憶體管理知識整理:

1.Java的記憶體管理

    Java的記憶體管理就是物件的分配和釋放問題。

    1). 分配:  記憶體的分配是由程式完成的,程式設計師需要通過關鍵字new 為每個物件申請記憶體空間,基本型別除外。所有的物件都在堆(Heap)中分配空間。

    2). 釋放:  物件的釋放是由垃圾回收機制(GC)決定和執行的,這樣做簡化了程式設計師的工作,但是同時卻加重了JVM的工作。(因為,GC為了能夠正確釋放物件,GC必須監控每一個物件的執行狀態,包括物件的申請、引用、被引用、賦值等,GC都需要進行監控。)

2.什麼叫java的記憶體洩露

    在java中,記憶體洩漏就是存在一些被分配的物件,這些物件有下面兩個特點,首先,這些物件是可達的,即在有向圖中,存在通路可以與其相連(也就是說仍存在該記憶體物件的引用);其次,這些物件是無用的,即程式以後不會再使用這些物件。如果物件滿足這兩個條件,這些物件就可以判定為Java中的記憶體洩漏,這些物件不會被GC所回收,然而它卻佔用記憶體。

3.垃圾回收機制

(1). 什麼叫垃圾回收機制?
垃圾回收是一種動態儲存管理技術,它自動地釋放不再被程式引用的物件,按照特定的垃圾收集演算法來實現資源自動回收的功能。當一個物件不再被引用的時候,記憶體回收它佔領的空間,以便空間被後來的新物件使用,以免造成記憶體洩露。

(2). java的垃圾回收有什麼特點?
java語言不允許程式設計師直接控制記憶體空間的使用。記憶體空間的分配和回收都是由JRE負責在後臺自動進行的,尤其是無用記憶體空間的回收操作(garbagecollection,也稱垃圾回收),只能由執行環境提供的一個超級執行緒進行監測和控制。


(3). 垃圾回收器什麼時候會執行? 
一般是在CPU空閒或空間不足時自動進行垃圾回收,而程式設計師無法精確控制垃圾回收的時機和順序等。

(4). 什麼樣的物件符合垃圾回收條件?
當沒有任何獲得執行緒能訪問一個物件時,該物件就符合垃圾回收條件。

(5). 垃圾回收器是怎樣工作的?
垃圾回收器如發現一個物件不能被任何活執行緒訪問時,他將認為該物件符合刪除條件,就將其加入回收佇列,但不是立即銷燬物件,何時銷燬並釋放記憶體是無法預知的。垃圾回收不能強制執行,然而java提供了一些方法(如:System.gc()方法),允許你請求JVM執行垃圾回收,而不是要求,虛擬機器會盡其所能滿足請求,但是不能保證JVM從記憶體中刪除所有不用的物件。

(6). 一個java程式能夠耗盡記憶體嗎? 
可以。垃圾收集系統嘗試在物件不被使用時把他們從記憶體中刪除。然而,如果保持太多活的物件,系統則可能會耗盡記憶體。垃圾回收器不能保證有足夠的記憶體,只能保證可用記憶體儘可能的得到高效的管理。

(7). 如何顯示的使物件符合垃圾回收條件? 

  a.空引用:當物件沒有對他可到達引用時,他就符合垃圾回收的條件。也就是說如果沒有對他的引用,刪除物件的引用就可以達到目的,因此我們可以把引用變數設定為null,來符合垃圾回收的條件。

    StringBuffer sb = new StringBuffer("hello");
    System.out.println(sb);
    sb= null;

  b.重新為引用變數賦值:可以通過設定引用變數引用另一個物件來解除該引用變數與一個物件間的引用關係。 
    StringBuffer sb1 = new StringBuffer(“hello”);
    StringBuffer sb2 = new StringBuffer(“goodbye”); 
    System.out.println(sb1);
    sb1=sb2;//此時”hello”符合回收條件


  c.方法內建立的物件:所建立的區域性變數僅在該方法的作用期間記憶體在。一旦該方法返回,在這個方法內建立的物件就符合垃圾收集條件。有一種明顯的例外情況,就是方法的返回物件。

    public static void main(String[] args) {
        Date d = getDate();
        System.out.println("d="+d);
    }
    private static Date getDate() {
        Date d2 = new Date();
        StringBuffer now = new StringBuffer(d2.toString());
        System.out.println(now);
        return d2;
    }


  d.隔離引用:這種情況中,被回收的物件仍具有引用,這種情況稱作隔離島。若存在這兩個例項,他們互相引用,並且這兩個物件的所有其他引用都刪除,其他任何執行緒無法訪問這兩個物件中的任意一個。也可以符合垃圾回收條件。

    public class Island {
        Island i;
        public static void main(String[] args) {
            Island i2 = new Island();
            Island i3 = new Island();
            Island i4 = new Island();
            i2. i =i3;
            i3. i =i4;
            i4. i =i2;
            i2= null;
            i3= null;
            i4= null;
        }
    }

(8). 垃圾收集前進行清理——finalize()方法 

java提供了一種機制,使你能夠在物件剛要被垃圾回收之前執行一些程式碼。這段程式碼位於名為finalize()的方法內,所有類從Object類繼承這個方法。由於不能保證垃圾回收器會刪除某個物件。因此放在finalize()中的程式碼無法保證執行。因此建議不要重寫finalize()。

4. JVM的記憶體區域組成
java把記憶體分兩種:
  棧記憶體
    在函式中定義的基本型別變數(即基本型別的區域性變數)和物件的引用變數(即物件的變數名)都在函式的棧記憶體中分配
  堆記憶體
    堆記憶體用來存放由new建立的物件和陣列以及物件的例項變數(即全域性變數)

在函式(程式碼塊)中定義一個變數時,java就在棧中為這個變數分配記憶體空間,當超過變數的作用域後,java會自動釋放掉為該變數所分配的記憶體空間;
在堆中分配的記憶體由java虛擬機器的自動垃圾回收器來管理

堆和棧的優缺點

堆的優勢  是可以動態分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶體的。
        缺點  要在執行時動態分配記憶體,存取速度較慢;

棧的優勢  存取速度比堆要快,僅次於直接位於CPU中的暫存器。另外,棧資料可以共享。
        缺點  存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。

此外補充一下java中還有的一個方法區
方法區中主要儲存所有物件資料共享區域,儲存靜態變數和普通方法、靜態方法、常量、字串常量(嚴格說存放在常量池,堆和棧都有)等類資訊,說白了就是儲存類的模板。

// 其他地方看到的,順便放出來參考下。
堆區:
a.儲存的全部是物件,每個物件都包含一個與之對應的class的資訊。(class的目的是得到操作指令)
b.jvm只有一個堆區(heap)被所有執行緒共享,堆中不存放基本型別和物件引用,只存放物件本身
棧區:
a.每個執行緒包含一個棧區,棧中只儲存基礎資料型別的物件和自定義物件的引用(不是物件),物件都存放在堆區中
b.每個棧中的資料(原始型別和物件引用)都是私有的,其他棧不能訪問。
c.棧分為3個部分:基本型別變數區、執行環境上下文、操作指令區(存放操作指令)。
方法區:
a.又叫靜態區,跟堆一樣,被所有的執行緒共享。方法區包含所有的class和static變數。
b.方法區中包含的都是在整個程式中永遠唯一的元素,如class,static變數。

5.java中資料在記憶體中是如何儲存的

a)基本資料型別


java的基本資料型別共有8種,即int,short,long,byte,float,double,boolean,char(注意,並沒有String的基本型別 )。這種型別的定義是通過諸如int a = 3;long b = 255L;的形式來定義的。如int a = 3;這裡的a是一個指向int型別的引用,指向3這個字面值。這些字面值的資料,由於大小可知,生存期可知(這些字面值定義在某個程式塊裡面,程式塊退出後,欄位值就消失了),出於追求速度的原因,就存在於棧中。

另外,棧有一個很重要的特殊性,就是存在棧中的資料可以共享。比如: 我們同時定義:

int a = 3;
int b =3;

編譯器先處理 int a = 3;首先它會在棧中建立一個變數為a的引用,然後查詢有沒有字面值為3的地址,沒找到,就開闢一個存放3這個字面值的地址,然後將a指向3的地址。接著處理int b = 3;在建立完b這個引用變數後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現了a與b同時均指向3的情況。

定義完a與b的值後,再令a = 4;那麼,b不會等於4,還是等於3。在編譯器內部,遇到時,它就會重新搜尋棧中是否有4的字面值,如果沒有,重新開闢地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。

b)物件

在java中,建立一個物件包括物件的宣告和例項化兩步,下面用一個例題來說明物件的記憶體模型。假設有類Rectangle定義如下:

    public class Rectangle {
        double width;
        double height;
        public Rectangle( double w, double h){
            w = width;
            h = height;
        }
    }

    (1)宣告物件時的記憶體模型 用Rectangle rect;宣告一個物件rect時,將在棧記憶體為物件的引用變數rect分配記憶體空間,但Rectangle的值為空,稱rect是一個空物件。空物件不能使用,因為它還沒有引用任何”實體”。 

    (2)物件例項化時的記憶體模型 當執行rect=new Rectangle(3,5);時,會做兩件事:在堆記憶體中為類的成員變數width,height分配記憶體,並將其初始化為各資料型別的預設值;接著進行顯式初始化(類定義時的初始化值);最後呼叫構造方法,為成員變數賦值。返回堆記憶體中物件的引用(相當於首地址)給引用變數rect,以後就可以通過rect來引用堆記憶體中的物件了。

c)建立多個不同的物件例項

一個類通過使用new運算子可以建立多個不同的物件例項,這些物件例項將在堆中被分配不同的記憶體空間,改變其中一個物件的狀態不會影響其他物件的狀態。例如:

    Rectangle r1 = new Rectangle(3, 5);
    Rectangle r2 = new Rectangle(4, 6);

此時,將在堆記憶體中分別為兩個物件的成員變數 width 、 height 分配記憶體空間,兩個物件在堆記憶體中佔據的空間是互不相同的。如果有:

    Rectangle r1 = new Rectangle(3, 5);
    Rectangle r2 = r1;

則在堆記憶體中只建立了一個物件例項,在棧記憶體中建立了兩個物件引用,兩個物件引用同時指向一個物件例項。

d)包裝類

基本型別都有對應的包裝類:如int對應Integer類,double對應Double類等,基本型別的定義都是直接在棧中,如果用包裝類來建立物件,就和普通物件一樣了。例如:int i=0;i直接儲存在棧中。Integer i(i此時是物件)= new Integer(5);這樣,i物件資料儲存在堆中,i的引用儲存在棧中,通過棧中的引用來操作物件。

e)String

String是一個特殊的包裝類資料。可以用以下兩種方式建立:String str = new String(“abc”);String str = “abc”; 第一種建立方式,和普通物件的的建立過程一樣; 第二種建立方式,java內部將此語句轉化為以下幾個步驟: 

(1)先定義一個名為str的對String類的物件引用變數:String str; 

(2)在棧中查詢有沒有存放值為”abc”的地址,如果沒有,則開闢一個存放字面值為”abc” 地址,接著建立一個新的String類的物件o,並將o的字串值指向這個地址,而且在棧 這個地址旁邊記下這個引用的物件o。如果已經有了值為”abc”的地址,則查詢物件o,並 回o的地址。

 (3)將str指向物件o的地址。 值得注意的是,一般String類中字串值都是直接存值的。但像String str = “abc”;這種 合下,其字串值卻是儲存了一個指向存在棧中資料的引用。 為了更好地說明這個問題,我們可以通過以下的幾個程式碼進行驗證。

    String str1 = "abc";
    String str2 = "abc";
    System.out.println(s1==s2);//true

注意,這裡並不用 str1.equals(str2);的方式,因為這將比較兩個字串的值是否相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個物件時才返回真值。而我們在這裡要看的是,str1與str2是否都指向了同一個物件。 我們再接著看以下的程式碼。

    String str1 = new String("abc");
    String str2 = "abc";
    System.out.println(str1 == str2);//false

建立了兩個引用。建立了兩個物件。兩個引用分別指向不同的兩個物件。 以上兩段程式碼說明,只要是用new()來新建物件的,都會在堆中建立,而且其字串是單獨存值的,即使與棧中的資料相同,也不會與棧中的資料共享。

f)陣列

當定義一個數組,int x[];或int[] x;時,在棧記憶體中建立一個數組引用,通過該引用(即陣列名)來引用陣列。x=new int[3];將在堆記憶體中分配3個儲存 int型資料的空間,堆記憶體的首地址放到棧記憶體中,每個陣列元素被初始化為0。

g)靜態變數

用static的修飾的變數和方法,實際上是指定了這些變數和方法在記憶體中的”固定位置”-static storage,可以理解為所有例項物件共有的記憶體空間。static變數有點類似於C中的全域性變數的概念;靜態表示的是記憶體的共享,就是它的每一個例項都指向同一個記憶體地址。把static拿來,就是告訴JVM它是靜態的,它的引用(含間接引用)都是指向同一個位置,在那個地方,你把它改了,它就不會變成原樣,你把它清理了,它就不會回來了。

那靜態變數與方法是在什麼時候初始化的呢?對於兩種不同的類屬性,static屬性與instance屬性,初始化的時機是不同的。instance屬性在建立例項的時候初始化,static屬性在類載入,也就是第一次用到這個類的時候初始化,對於後來的例項的建立,不再次進行初始化。

我們常可看到類似以下的例子來說明這個問題:

    class Student{
        static int numberOfStudents =0;
        Student()
        {
            numberOfStudents ++;
        }
    }

每一次建立一個新的Student例項時,成員numberOfStudents都會不斷的遞增,並且所有的Student例項都訪問同一個numberOfStudents變數,實際上intnumberOfStudents變數在記憶體中只儲存在一個位置上。

6.java的記憶體管理例項

Java程式的多個部分(方法,變數,物件)駐留在記憶體中以下兩個位置:即堆和棧,現在我們只關心三類事物:例項變數,區域性變數和物件: 例項變數和物件駐留在堆上 區域性變數駐留在棧上 讓我們檢視一個 java 程式,看看他的各部分如何建立並且對映到棧和堆中:

    public class Dog {
        Collar c;
        String name;
        //1.main()方法位於棧上
        public static void main(String[] args) {
            //2.在棧上建立引用變數d,但Dog物件尚未存在
            Dog d;
            //3.建立新的Dog物件,並將其賦予d引用變數
            d = new Dog();
            //4.將引用變數的一個副本傳遞給go()方法
            d.go(d);
        }

        //5.將go()方法置於棧上,並將dog引數作為區域性變數
        void go(Dog dog) {
            //6.在堆上建立新的Collar物件,並將其賦予Dog的例項變數
            c = new Collar();
        }

        //7.將setName()新增到棧上,並將dogName引數作為其區域性變數
        void setName(String dogName) {
            //8.name的例項物件也引用String物件
            name = dogName;
        }
        //9.程式執行完成後,setName()將會完成並從棧中清除,此時,區域性變數dogName也會消失,儘管它所引用的String仍在堆上
    }


7.final問題

final使得被修飾的變數”不變”,但是由於物件型變數的本質是”引用”,使得”不變”也有了兩種含義:引用本身的不變和引用指向的物件不變。 引用本身的不變:

    final StringBuffer a = new StringBuffer("immutable");
    final StringBuffer b = new StringBuffer("not immutable");
    a=b;//編譯期錯誤
    final StringBuffer a = new StringBuffer("immutable");
    final StringBuffer b = new StringBuffer("not immutable");
    a=b;//編譯期錯誤

引用指向的物件不變:

    final StringBuffer a = new StringBuffer("immutable");
    a.append("broken!");//編譯通過
    final StringBuffer a = new StringBuffer("immutable");
    a.append("broken!");//編譯通過

可見,final只對引用的”值”(也即它所指向的那個物件的記憶體地址)有效,它迫使引用只能指向初始指向的那個物件,改變它的指向會導致編譯期錯誤。至於它所指向的物件的變化,final是不負責的。這很類似==操作符:==操作符只負責引用的”值”相等,至於這個地址所指向的物件內容是否相等,==操作符是不管的。在舉一個例子:

    public class Name {
        private String firstname;
        private String lastname;
        public String getFirstname() {
            return firstname;
        }
        public void setFirstname(String firstname) {
            this.firstname = firstname;
        }
        public String getLastname() {
            return lastname;
        }
        public void setLastname(String lastname) {
            this.lastname = lastname;
        }
    }

編寫測試方法:

    public static void main(String[] args) {
        final Name name = new Name();
        name.setFirstname("JIM");
        name.setLastname("Green");
        System.out.println(name.getFirstname()+ " " +name.getLastname());
    }
    public static void main(String[] args) {
        final Name name = new Name();
        name.setFirstname("JIM");
        name.setLastname("Green");
        System.out.println(name.getFirstname()+" "+name.getLastname());
    }

理解final問題有很重要的含義。許多程式漏洞都基於此—-final只能保證引用永遠指向固定物件,不能保證那個物件的狀態不變。在多執行緒的操作中,一個物件會被多個執行緒共享或修改,一個執行緒對物件無意識的修改可能會導致另一個使用此物件的執行緒崩潰。一個錯誤的解決方法就是在此物件新建的時候把它宣告為final,意圖使得它”永遠不變”。其實那是徒勞的.final還有一個值得注意的地方, 先看以下示例程式:

    class Something {
        final int i ;
        public void doSomething() {
            System. out .println( "i = " + i );
        }
    }
    class Something {
        final int i;
        public void doSomething() {
            System.out.println("i = " + i);
        }
    }

對於類變數,java虛擬機器會自動進行初始化。如果給出了初始值,則初始化為該初始值。如果沒有給出,則把它初始化為該型別變數的預設初始值。但是對於用final修飾的類變數,虛擬機器不會為其賦予初值,必須在constructor(構造器)結束之前被賦予一個明確的值。可以修改為”final int i = 0;”。

8.如何把程式寫得更健壯

(1)儘早釋放無用物件的引用。 好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後,自動設定為null,暗示垃圾收集器來收集該物件,防止發生記憶體洩露。對於仍然有指標指向的例項,jvm就不會回收該資源,因為垃圾回收會將值為null的物件作為垃圾,提高GC回收機制效率;
(2)定義字串應該儘量使用String str=”hello”;的形式,避免使用String str = new String(“hello”);的形式。因為要使用內容相同的字串,不必每次都new一個String。例如我們要在構造器中對一個名叫s的String引用變數進行初始化,把它設定為初始值,應當這樣做:

    public class Demo {
        private String s;
        public Demo() {
            s = "Initial Value";
        }
    }
    public class Demo {
        private String s;
        …
        public Demo {
            s = "Initial Value";
        }
        …
    }
    // 而非 
    s =  new  String("Initial Value");
    s = new String("Initial Value");

後者每次都會呼叫構造器,生成新物件,效能低下且記憶體開銷大,並且沒有意義,因為String物件不可改變,所以對於內容相同的字串,只要一個String物件來表示就可以了。也就說,多次呼叫上面的構造器建立多個物件,他們的String型別屬性s都指向同一個物件。
(3)我們的程式裡不可避免大量使用字串處理,避免使用String,應大量使用StringBuffer,因為String被設計成不可變(immutable)類,所以它的所有物件都是不可變物件,請看下列程式碼;

    String s = "Hello";
    s = s + " world!";
    String s = "Hello";
    s = s + " world!";

在這段程式碼中,s原先指向一個String物件,內容是”Hello”,然後我們對s進行了+操作,那麼s所指向的那個物件是否發生了改變呢?答案是沒有。這時,s不指向原來那個物件了,而指向了另一個String物件,內容為”Hello world!”,原來那個物件還存在於記憶體之中,只是s這個引用變數不再指向它了。
通過上面的說明,我們很容易匯出另一個結論,如果經常對字串進行各種各樣的修改,或者說,不可預見的修改,那麼使用String來代表字串的話會引起很大的記憶體開銷。因為String物件建立之後不能再改變,所以對於每一個不同的字串,都需要一個String物件來表示。這時,應該考慮使用StringBuffer類,它允許修改,而不是每個不同的字串都要生成一個新的物件。並且,這兩種類的物件轉換十分容易。
(4)儘量少用靜態變數,因為靜態變數是全域性的,GC不會回收的;
(5)儘量避免在類的建構函式裡建立、初始化大量的物件,防止在呼叫其自身類的構造器時造成不必要的記憶體資源浪費,尤其是大物件,JVM會突然需要大量記憶體,這時必然會觸發GC優化系統記憶體環境;顯示的宣告陣列空間,而且申請數量還極大。 以下是初始化不同型別的物件需要消耗的時間:

運算操作

示例

標準化時間

本地賦值

i = n

1.0

例項賦值

this.i = n

1.2

方法呼叫

Funct()

5.9

新建物件

New Object()

980

新建陣列

New int[10]

3100



從表中可以看出,新建一個物件需要980個單位的時間,是本地賦值時間的980倍,是方法呼叫時間的166倍,而新建一個數組所花費的時間就更多了。
(6)儘量在合適的場景下使用物件池技術以提高系統性能,縮減縮減開銷,但是要注意物件池的尺寸不宜過大,及時清除無效物件釋放記憶體資源,綜合考慮應用執行環境的記憶體資源限制,避免過高估計執行環境所提供記憶體資源的數量。
(7)大集合物件擁有大資料量的業務物件的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。
(8)不要在經常呼叫的方法中建立物件,尤其是忌諱在迴圈中建立物件。可以適當的使用hashtable,vector建立一組物件容器,然後從容器中去取那些物件,而不用每次new之後又丟棄。
(9)一般都是發生在開啟大型檔案或跟資料庫一次拿了太多的資料,造成Out Of Memory Error的狀況,這時就大概要計算一下資料量的最大值是多少,並且設定所需最小及最大的記憶體空間值。
(10)儘量少用finalize函式,因為finalize()會加大GC的工作量,而GC相當於耗費系統的計算能力。

(11)不要過濫使用雜湊表,有一定開發經驗的開發人員經常會使用hash表(hash表在JDK中的一個實現就是HashMap)來快取一些資料,從而提高系統的執行速度。比如使用HashMap快取一些物料資訊、人員資訊等基礎資料,這在提高系統速度的同時也加大了系統的記憶體佔用,特別是當快取的資料比較多的時候。其實我們可以使用作業系統中的快取的概念來解決這個問題,也就是給被快取的分配一個一定大小的快取容器,按照一定的演算法淘汰不需要繼續快取的物件,這樣一方面會因為進行了物件快取而提高了系統的執行效率,同時由於快取容器不是無限制擴大,從而也減少了系統的記憶體佔用。現在有很多開源的快取實現專案,比如ehcache、oscache等,這些專案都實現了FIFO 、MRU等常見的快取演算法。

本篇文章是根據查閱一些資料和技術部落格後所整理完成的,如有描述不準確的,請留言提出。以免誤導大家。