posts - 495,  comments - 11,  trackbacks - 0

          1. java是如何管理內(nèi)存的

          ?Java的內(nèi)存管理就是對(duì)象的分配和釋放問(wèn)題。(兩部分)

          分配 :內(nèi)存的分配是由程序完成的,程序員需要通過(guò)關(guān)鍵字new 為每個(gè)對(duì)象申請(qǐng)內(nèi)存空間 (基本類型除外),所有的對(duì)象都在堆 (Heap)中分配空間。
          釋放 :對(duì)象的釋放是由垃圾回收機(jī)制決定和執(zhí)行的,這樣做確實(shí)簡(jiǎn)化了程序員的工作。但同時(shí),它也加重了JVM的工作。因?yàn)?,GC為了能夠正確釋放對(duì)象,GC必須監(jiān)控每一個(gè)對(duì)象的運(yùn)行狀態(tài),包括對(duì)象的申請(qǐng)、引用、被引用、賦值等,GC都需要進(jìn)行監(jiān)控。

          2. 什么叫java的內(nèi)存泄露

          ???? 在Java中,內(nèi)存泄漏就是存在一些被分配的對(duì)象,這些對(duì)象有下面兩個(gè)特點(diǎn),首先,這些對(duì)象是可達(dá)的,即在有向圖中,存在通路可以與其相連(也就是說(shuō)仍存在該內(nèi)存對(duì)象的引用);其次,這些對(duì)象是無(wú)用的,即程序以后不會(huì)再使用這些對(duì)象。如果對(duì)象滿足這兩個(gè)條件,這些對(duì)象就可以判定為Java中的內(nèi)存泄漏,這些對(duì)象不會(huì)被GC所回收,然而它卻占用內(nèi)存。

          3. JVM的內(nèi)存區(qū)域組成

          java把內(nèi)存分兩種:一種是棧內(nèi)存,另一種是堆內(nèi)存1。在函數(shù)中定義的基本類型變量和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配;2。堆內(nèi)存用來(lái)存放由new創(chuàng)建的對(duì)象和數(shù)組以及對(duì)象的實(shí)例變量 在函數(shù)(代碼塊)中定義一個(gè)變量時(shí),java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過(guò)變量的作用域后,java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間;在堆中分配的內(nèi)存由java虛擬機(jī)的自動(dòng)垃圾回收器來(lái)管理
          堆和棧的優(yōu)缺點(diǎn)???

          ?堆的優(yōu)勢(shì)是可以動(dòng)態(tài)分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的。

          缺點(diǎn)就是要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢; 棧的優(yōu)勢(shì)是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。

          另外,棧數(shù)據(jù)可以共享。但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。

          4. Java中數(shù)據(jù)在內(nèi)存中是如何存儲(chǔ)的

          a) 基本數(shù)據(jù)類型

          ?? Java的基本數(shù)據(jù)類型共有8種,即int, short, long, byte, float, double, boolean, char(注意,并沒有string的基本類型)。這種類型的定義是通過(guò)諸如int a = 3; long b = 255L;的形式來(lái)定義的。如int a = 3;這里的a是一個(gè)指向int類型的引用,指向3這個(gè)字面值。這些字面值的數(shù)據(jù),由于大小可知,生存期可知(這些字面值定義在某個(gè)程序塊里面,程序塊退出后,字段值就消失了),出于追求速度的原因,就存在于棧中。
          另外,棧有一個(gè)很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。比如:我們同時(shí)定義:
          int a=3;
          int b =3;
          ??? 編譯器先處理int a = 3;首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a的引用,然后查找有沒有字面值為3的地址,沒找到,就開辟一個(gè)存放3這個(gè)字面值的地址,然后將a指向3的地址。接著處理int b = 3;在創(chuàng)建完b這個(gè)引用變量后,由于在棧中已經(jīng)有3這個(gè)字面值,便將b直接指向3的地址。這樣,就出現(xiàn)了a與b同時(shí)均指向3的情況。??? 定義完a與b的值后,再令a = 4;那么,b不會(huì)等于4,還是等于3。在編譯器內(nèi)部,遇到時(shí),它就會(huì)重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經(jīng)有了,則直接將a指向這個(gè)地址。因此a值的改變不會(huì)影響到b的值。

          b)??? 對(duì)象

          在Java中,創(chuàng)建一個(gè)對(duì)象包括對(duì)象的聲明和實(shí)例化兩步,下面用一個(gè)例題來(lái)說(shuō)明對(duì)象的內(nèi)存模型?! 〖僭O(shè)有類Rectangle定義如下:
          public class Rectangle {
          double width;
          double height;
          public Rectangle(double w,double h){
          w = width;
          h = height;
          }
          }
          (1)聲明對(duì)象時(shí)的內(nèi)存模型
           用Rectangle rect;聲明一個(gè)對(duì)象rect時(shí),將在棧內(nèi)存為對(duì)象的引用變量rect分配內(nèi)存空間,但Rectangle的值為空,稱rect是一個(gè)空對(duì)象。空對(duì)象不能使用,因?yàn)樗€沒有引用任何"實(shí)體"。
          (2)對(duì)象實(shí)例化時(shí)的內(nèi)存模型
           當(dāng)執(zhí)行rect=new Rectangle(3,5);時(shí),會(huì)做兩件事: 在堆內(nèi)存中為類的成員變量width,height分配內(nèi)存,并將其初始化為各數(shù)據(jù)類型的默認(rèn)值;接著進(jìn)行顯式初始化(類定義時(shí)的初始化值);最后調(diào)用構(gòu)造方法,為成員變量賦值。? 返回堆內(nèi)存中對(duì)象的引用(相當(dāng)于首地址)給引用變量rect,以后就可以通過(guò)rect來(lái)引用堆內(nèi)存中的對(duì)象了。

          c)??? 創(chuàng)建多個(gè)不同的對(duì)象實(shí)例

          ??????? 一個(gè)類通過(guò)使用new運(yùn)算符可以創(chuàng)建多個(gè)不同的對(duì)象實(shí)例,這些對(duì)象實(shí)例將在堆中被分配不同的內(nèi)存空間,改變其中一個(gè)對(duì)象的狀態(tài)不會(huì)影響其他對(duì)象的狀態(tài)。例如:
          Rectangle r1= new Rectangle(3,5);
          Rectangle r2= new Rectangle(4,6);
           此時(shí),將在堆內(nèi)存中分別為兩個(gè)對(duì)象的成員變量width、height分配內(nèi)存空間,兩個(gè)對(duì)象在堆內(nèi)存中占據(jù)的空間是互不相同的。如果有:
          Rectangle r1= new Rectangle(3,5);
          Rectangle r2=r1;
          則在堆內(nèi)存中只創(chuàng)建了一個(gè)對(duì)象實(shí)例,在棧內(nèi)存中創(chuàng)建了兩個(gè)對(duì)象引用,兩個(gè)對(duì)象引用同時(shí)指向一個(gè)對(duì)象實(shí)例。
          ?

          d)??? 包裝類

          ???????? 基本型別都有對(duì)應(yīng)的包裝類:如int對(duì)應(yīng)Integer類,double對(duì)應(yīng)Double類等,基本類型的定義都是直接在棧中,如果用包裝類來(lái)創(chuàng)建對(duì)象,就和普通對(duì)象一樣了。例如:int i=0;i直接存儲(chǔ)在棧中。? Integer i(i此時(shí)是對(duì)象) = new Integer(5);這樣,i對(duì)象數(shù)據(jù)存儲(chǔ)在堆中,i的引用存儲(chǔ)在棧中,通過(guò)棧中的引用來(lái)操作對(duì)象。
          ?

          e)??? String

          ? String是一個(gè)特殊的包裝類數(shù)據(jù)??梢杂糜靡韵聝煞N方式創(chuàng)建:String str = new String("abc");String str = "abc";
          第一種創(chuàng)建方式,和普通對(duì)象的的創(chuàng)建過(guò)程一樣;
          第二種創(chuàng)建方式,Java內(nèi)部將此語(yǔ)句轉(zhuǎn)化為以下幾個(gè)步驟:
          (1) 先定義一個(gè)名為str的對(duì)String類的對(duì)象引用變量:String str;
          (2) 在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個(gè)存放字面值為"abc"
          地址,接著創(chuàng)建一個(gè)新的String類的對(duì)象o,并將o的字符串值指向這個(gè)地址,而且在棧
          這個(gè)地址旁邊記下這個(gè)引用的對(duì)象o。如果已經(jīng)有了值為"abc"的地址,則查找對(duì)象o,并
          回o的地址。
          (3) 將str指向?qū)ο髈的地址。
          值得注意的是,一般String類中字符串值都是直接存值的。但像String str = "abc";這種
          合下,其字符串值卻是保存了一個(gè)指向存在棧中數(shù)據(jù)的引用。
          為了更好地說(shuō)明這個(gè)問(wèn)題,我們可以通過(guò)以下的幾個(gè)代碼進(jìn)行驗(yàn)證。
          String str1="abc";
          String str2="abc";
          System.out.println(s1==s2);//true
          注意,這里并不用str1.equals(str2);的方式,因?yàn)檫@將比較兩個(gè)字符串的值是否相等。==號(hào),根據(jù)JDK的說(shuō)明,只有在兩個(gè)引用都指向了同一個(gè)對(duì)象時(shí)才返回真值。而我們?cè)谶@里要看的是,str1與str2是否都指向了同一個(gè)對(duì)象。
          我們?cè)俳又匆韵碌拇a。
          String str1= new String("abc");
          String str2="abc";
          System.out.println(str1==str2);//false
          創(chuàng)建了兩個(gè)引用。創(chuàng)建了兩個(gè)對(duì)象。兩個(gè)引用分別指向不同的兩個(gè)對(duì)象?! ?? 以上兩段代碼說(shuō)明,只要是用new()來(lái)新建對(duì)象的,都會(huì)在堆中創(chuàng)建,而且其字符串是單獨(dú)存值的,即使與棧中的數(shù)據(jù)相同,也不會(huì)與棧中的數(shù)據(jù)共享。

          f)??? 數(shù)組

          ???????? 當(dāng)定義一個(gè)數(shù)組,int x[];或int []x;時(shí),在棧內(nèi)存中創(chuàng)建一個(gè)數(shù)組引用,通過(guò)該引用(即數(shù)組名)來(lái)引用數(shù)組。x=new int[3];將在堆內(nèi)存中分配3個(gè)保存int型數(shù)據(jù)的空間,堆內(nèi)存的首地址放到棧內(nèi)存中,每個(gè)數(shù)組元素被初始化為0。
          ?

          g)??? 靜態(tài)變量

          ???????? 用static的修飾的變量和方法,實(shí)際上是指定了這些變量和方法在內(nèi)存中的"固定位置"-static storage,可以理解為所有實(shí)例對(duì)象共有的內(nèi)存空間。static變量有點(diǎn)類似于C中的全局變量的概念;靜態(tài)表示的是內(nèi)存的共享,就是它的每一個(gè)實(shí)例都指向同一個(gè)內(nèi)存地址。把static拿來(lái),就是告訴JVM它是靜態(tài)的,它的引用(含間接引用)都是指向同一個(gè)位置,在那個(gè)地方,你把它改了,它就不會(huì)變成原樣,你把它清理了,它就不會(huì)回來(lái)了。???????? 那靜態(tài)變量與方法是在什么時(shí)候初始化的呢?對(duì)于兩種不同的類屬性,static屬性與instance屬性,初始化的時(shí)機(jī)是不同的。instance屬性在創(chuàng)建實(shí)例的時(shí)候初始化,static屬性在類加載,也就是第一次用到這個(gè)類的時(shí)候初始化,對(duì)于后來(lái)的實(shí)例的創(chuàng)建,不再次進(jìn)行初始化。???????? 我們??煽吹筋愃埔韵碌睦觼?lái)說(shuō)明這個(gè)問(wèn)題:
          class Student{
          static int numberOfStudents=0;
          Student()
          {
          numberOfStudents++;
          }
          }
          每一次創(chuàng)建一個(gè)新的Student實(shí)例時(shí),成員numberOfStudents都會(huì)不斷的遞增,并且所有的Student實(shí)例都訪問(wèn)同一個(gè)numberOfStudents變量,實(shí)際上int numberOfStudents變量在內(nèi)存中只存儲(chǔ)在一個(gè)位置上。

          5. Java的內(nèi)存管理實(shí)例

          ? Java程序的多個(gè)部分(方法,變量,對(duì)象)駐留在內(nèi)存中以下兩個(gè)位置:即堆和棧,現(xiàn)在我們只關(guān)心3類事物:實(shí)例變量,局部變量和對(duì)象:
          實(shí)例變量和對(duì)象駐留在堆上
          局部變量駐留在棧上
          ?????? 讓我們查看一個(gè)java程序,看看他的各部分如何創(chuàng)建并且映射到棧和堆中:
          public class Dog {
          Collar c;
          String name;
          //1. main()方法位于棧上
          public static void main(String[] args) {
          //2. 在棧上創(chuàng)建引用變量d,但Dog對(duì)象尚未存在
          Dog d;
          //3. 創(chuàng)建新的Dog對(duì)象,并將其賦予d引用變量
          d = new Dog();
          //4. 將引用變量的一個(gè)副本傳遞給go()方法
          d.go(d);
          }
          //5. 將go()方法置于棧上,并將dog參數(shù)作為局部變量
          void go(Dog dog){
          //6. 在堆上創(chuàng)建新的Collar對(duì)象,并將其賦予Dog的實(shí)例變量
          c =new Collar();
          }
          //7.將setName()添加到棧上,并將dogName參數(shù)作為其局部變量
          void setName(String dogName){
          //8. name的實(shí)例對(duì)象也引用String對(duì)象
          name=dogName;
          }
          //9. 程序執(zhí)行完成后,setName()將會(huì)完成并從棧中清除,此時(shí),局部變量dogName也會(huì)消失,盡管它所引用的String仍在堆上
          }

          6. 垃圾回收機(jī)制:

          (問(wèn)題一:什么叫垃圾回收機(jī)制?) 垃圾回收是一種動(dòng)態(tài)存儲(chǔ)管理技術(shù),它自動(dòng)地釋放不再被程序引用的對(duì)象,按照特定的垃圾收集算法來(lái)實(shí)現(xiàn)資源自動(dòng)回收的功能。當(dāng)一個(gè)對(duì)象不再被引用的時(shí)候,內(nèi)存回收它占領(lǐng)的空間,以便空間被后來(lái)的新對(duì)象使用,以免造成內(nèi)存泄露。 (問(wèn)題二:java的垃圾回收有什么特點(diǎn)?) JAVA語(yǔ)言不允許程序員直接控制內(nèi)存空間的使用。內(nèi)存空間的分配和回收都是由JRE負(fù)責(zé)在后臺(tái)自動(dòng)進(jìn)行的,尤其是無(wú)用內(nèi)存空間的回收操作(garbagecollection,也稱垃圾回收),只能由運(yùn)行環(huán)境提供的一個(gè)超級(jí)線程進(jìn)行監(jiān)測(cè)和控制。 (問(wèn)題三:垃圾回收器什么時(shí)候會(huì)運(yùn)行?) 一般是在CPU空閑或空間不足時(shí)自動(dòng)進(jìn)行垃圾回收,而程序員無(wú)法精確控制垃圾回收的時(shí)機(jī)和順序等。 (問(wèn)題四:什么樣的對(duì)象符合垃圾回收條件?) 當(dāng)沒有任何獲得線程能訪問(wèn)一個(gè)對(duì)象時(shí),該對(duì)象就符合垃圾回收條件。 (問(wèn)題五:垃圾回收器是怎樣工作的?) 垃圾回收器如發(fā)現(xiàn)一個(gè)對(duì)象不能被任何活線程訪問(wèn)時(shí),他將認(rèn)為該對(duì)象符合刪除條件,就將其加入回收隊(duì)列,但不是立即銷毀對(duì)象,何時(shí)銷毀并釋放內(nèi)存是無(wú)法預(yù)知的。垃圾回收不能強(qiáng)制執(zhí)行,然而Java提供了一些方法(如:System.gc()方法),允許你請(qǐng)求JVM執(zhí)行垃圾回收,而不是要求,虛擬機(jī)會(huì)盡其所能滿足請(qǐng)求,但是不能保證JVM從內(nèi)存中刪除所有不用的對(duì)象。 (問(wèn)題六:一個(gè)java程序能夠耗盡內(nèi)存嗎?) 可以。垃圾收集系統(tǒng)嘗試在對(duì)象不被使用時(shí)把他們從內(nèi)存中刪除。然而,如果保持太多活的對(duì)象,系統(tǒng)則可能會(huì)耗盡內(nèi)存。垃圾回收器不能保證有足夠的內(nèi)存,只能保證可用內(nèi)存盡可能的得到高效的管理。 (問(wèn)題七:如何顯示的使對(duì)象符合垃圾回收條件?) (1) 空引用 :當(dāng)對(duì)象沒有對(duì)他可到達(dá)引用時(shí),他就符合垃圾回收的條件。也就是說(shuō)如果沒有對(duì)他的引用,刪除對(duì)象的引用就可以達(dá)到目的,因此我們可以把引用變量設(shè)置為null,來(lái)符合垃圾回收的條件。
          StringBuffer sb = new StringBuffer("hello");
          System.out.println(sb);
          sb=null;
          (2) 重新為引用變量賦值:可以通過(guò)設(shè)置引用變量引用另一個(gè)對(duì)象來(lái)解除該引用變量與一個(gè)對(duì)象間的引用關(guān)系。
          StringBuffer sb1 = new StringBuffer("hello");
          StringBuffer sb2 = new StringBuffer("goodbye");
          System.out.println(sb1);
          sb1=sb2;//此時(shí)"hello"符合回收條件?
          (3) 方法內(nèi)創(chuàng)建的對(duì)象:所創(chuàng)建的局部變量?jī)H在該方法的作用期間內(nèi)存在。一旦該方法返回,在這個(gè)方法內(nèi)創(chuàng)建的對(duì)象就符合垃圾收集條件。有一種明顯的例外情況,就是方法的返回對(duì)象。
          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;
          }
          (4) 隔離引用:這種情況中,被回收的對(duì)象仍具有引用,這種情況稱作隔離島。若存在這兩個(gè)實(shí)例,他們互相引用,并且這兩個(gè)對(duì)象的所有其他引用都刪除,其他任何線程無(wú)法訪問(wèn)這兩個(gè)對(duì)象中的任意一個(gè)。也可以符合垃圾回收條件。
          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;
          }
          }
          (問(wèn)題八:垃圾收集前進(jìn)行清理------finalize()方法) java提供了一種機(jī)制,使你能夠在對(duì)象剛要被垃圾回收之前運(yùn)行一些代碼。這段代碼位于名為finalize()的方法內(nèi),所有類從Object類繼承這個(gè)方法。由于不能保證垃圾回收器會(huì)刪除某個(gè)對(duì)象。因此放在finalize()中的代碼無(wú)法保證運(yùn)行。因此建議不要重寫finalize();
          7.??? final問(wèn)題:
          ????? final使得被修飾的變量"不變",但是由于對(duì)象型變量的本質(zhì)是"引用",使得"不變"也有了兩種含義:引用本身的不變?,和引用指向的對(duì)象不變。????????? 引用本身的不變:
          final StringBuffer a=new StringBuffer("immutable");
          final StringBuffer b=new StringBuffer("not immutable");
          a=b;//編譯期錯(cuò)誤
          final StringBuffer a=new StringBuffer("immutable");
          final StringBuffer b=new StringBuffer("not immutable");
          a=b;//編譯期錯(cuò)誤
          引用指向的對(duì)象不變:
          final StringBuffer a=new StringBuffer("immutable");
          a.append(" broken!"); //編譯通過(guò)
          final StringBuffer a=new StringBuffer("immutable");
          a.append(" broken!"); //編譯通過(guò)
          可見,final只對(duì)引用的"值"(也即它所指向的那個(gè)對(duì)象的內(nèi)存地址)有效,它迫使引用只能指向初始指向的那個(gè)對(duì)象,改變它的指向會(huì)導(dǎo)致編譯期錯(cuò)誤。至于它所指向的對(duì)象的變化,final是不負(fù)責(zé)的。這很類似==操作符:==操作符只負(fù)責(zé)引用的"值"相等,至于這個(gè)地址所指向的對(duì)象內(nèi)容是否相等,==操作符是不管的。在舉一個(gè)例子:
          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 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;
          }
          }
          ?
          ???????? 編寫測(cè)試方法:
          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問(wèn)題有很重要的含義。許多程序漏洞都基于此----final只能保證引用永遠(yuǎn)指向固定對(duì)象,不能保證那個(gè)對(duì)象的狀態(tài)不變。在多線程的操作中,一個(gè)對(duì)象會(huì)被多個(gè)線程共享或修改,一個(gè)線程對(duì)對(duì)象無(wú)意識(shí)的修改可能會(huì)導(dǎo)致另一個(gè)使用此對(duì)象的線程崩潰。一個(gè)錯(cuò)誤的解決方法就是在此對(duì)象新建的時(shí)候把它聲明為final,意圖使得它"永遠(yuǎn)不變"。其實(shí)那是徒勞的。???????? Final還有一個(gè)值得注意的地方:???????? 先看以下示例程序:
          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);
          }
          }
          ???????? 對(duì)于類變量,Java虛擬機(jī)會(huì)自動(dòng)進(jìn)行初始化。如果給出了初始值,則初始化為該初始值。如果沒有給出,則把它初始化為該類型變量的默認(rèn)初始值。但是對(duì)于用final修飾的類變量,虛擬機(jī)不會(huì)為其賦予初值,必須在constructor (構(gòu)造器)結(jié)束之前被賦予一個(gè)明確的值。可以修改為"final int i = 0;"。
          ?
          8.??? 如何把程序?qū)懙酶眩?/span>
          ???? 1、盡早釋放無(wú)用對(duì)象的引用。 好的辦法是使用臨時(shí)變量的時(shí)候,讓引用變量在退出活動(dòng)域后,自動(dòng)設(shè)置為null,暗示垃圾收集器來(lái)收集該對(duì)象,防止發(fā)生內(nèi)存泄露。對(duì)于仍然有指針指向的實(shí)例,jvm就不會(huì)回收該資源,因?yàn)槔厥諘?huì)將值為null的對(duì)象作為垃圾,提高GC回收機(jī)制效率;
          ???? 2、定義字符串應(yīng)該盡量使用 String str="hello"; 的形式 ,避免使用String str = new String("hello"); 的形式。因?yàn)橐褂脙?nèi)容相同的字符串,不必每次都new一個(gè)String。例如我們要在構(gòu)造器中對(duì)一個(gè)名叫s的String引用變量進(jìn)行初始化,把它設(shè)置為初始值,應(yīng)當(dāng)這樣做:
          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");
          ???? 后者每次都會(huì)調(diào)用構(gòu)造器,生成新對(duì)象,性能低下且內(nèi)存開銷大,并且沒有意義,因?yàn)镾tring對(duì)象不可改變,所以對(duì)于內(nèi)容相同的字符串,只要一個(gè)String對(duì)象來(lái)表示就可以了。也就說(shuō),多次調(diào)用上面的構(gòu)造器創(chuàng)建多個(gè)對(duì)象,他們的String類型屬性s都指向同一個(gè)對(duì)象。???

          3、我們的程序里不可避免大量使用字符串處理,避免使用String,應(yīng)大量使用StringBuffer ,因?yàn)镾tring被設(shè)計(jì)成不可變(immutable)類,所以它的所有對(duì)象都是不可變對(duì)象,請(qǐng)看下列代碼;
          String s = "Hello";??
          s = s + " world!";?
          String s = "Hello";
          s = s + " world!";
          ?????? 在這段代碼中,s原先指向一個(gè)String對(duì)象,內(nèi)容是 "Hello",然后我們對(duì)s進(jìn)行了+操作,那么s所指向的那個(gè)對(duì)象是否發(fā)生了改變呢?答案是沒有。這時(shí),s不指向原來(lái)那個(gè)對(duì)象了,而指向了另一個(gè) String對(duì)象,內(nèi)容為"Hello world!",原來(lái)那個(gè)對(duì)象還存在于內(nèi)存之中,只是s這個(gè)引用變量不再指向它了。???????? 通過(guò)上面的說(shuō)明,我們很容易導(dǎo)出另一個(gè)結(jié)論,如果經(jīng)常對(duì)字符串進(jìn)行各種各樣的修改,或者說(shuō),不可預(yù)見的修改,那么使用String來(lái)代表字符串的話會(huì)引起很大的內(nèi)存開銷。因?yàn)?String對(duì)象建立之后不能再改變,所以對(duì)于每一個(gè)不同的字符串,都需要一個(gè)String對(duì)象來(lái)表示。這時(shí),應(yīng)該考慮使用StringBuffer類,它允許修改,而不是每個(gè)不同的字符串都要生成一個(gè)新的對(duì)象。并且,這兩種類的對(duì)象轉(zhuǎn)換十分容易。
          ???? 4、盡量少用靜態(tài)變量 ,因?yàn)殪o態(tài)變量是全局的,GC不會(huì)回收的;
          ???? 5、盡量避免在類的構(gòu)造函數(shù)里創(chuàng)建、初始化大量的對(duì)象 ,防止在調(diào)用其自身類的構(gòu)造器時(shí)造成不必要的內(nèi)存資源浪費(fèi),尤其是大對(duì)象,JVM會(huì)突然需要大量?jī)?nèi)存,這時(shí)必然會(huì)觸發(fā)GC優(yōu)化系統(tǒng)內(nèi)存環(huán)境;顯示的聲明數(shù)組空間,而且申請(qǐng)數(shù)量還極大。???????? 以下是初始化不同類型的對(duì)象需要消耗的時(shí)間:

          運(yùn)算操作??
          ?示例???
          ?標(biāo)準(zhǔn)化時(shí)間
          ?
          本地賦值???
          ?i = n
          ?1.0
          ?
          實(shí)例賦值???
          ?this.i = n
          ?1.2
          ?
          方法調(diào)用???
          ?Funct()
          ?5.9
          ?
          新建對(duì)象???
          ?New Object()
          ?980
          ?
          新建數(shù)組???
          ?New int[10]
          ?3100
          ?

          ???????
          從表1可以看出,新建一個(gè)對(duì)象需要980個(gè)單位的時(shí)間,是本地賦值時(shí)間的980倍,是方法調(diào)用時(shí)間的166倍,而新建一個(gè)數(shù)組所花費(fèi)的時(shí)間就更多了。
          ???? 6、盡量在合適的場(chǎng)景下使用對(duì)象池技術(shù) 以提高系統(tǒng)性能,縮減縮減開銷,但是要注意對(duì)象池的尺寸不宜過(guò)大,及時(shí)清除無(wú)效對(duì)象釋放內(nèi)存資源,綜合考慮應(yīng)用運(yùn)行環(huán)境的內(nèi)存資源限制,避免過(guò)高估計(jì)運(yùn)行環(huán)境所提供內(nèi)存資源的數(shù)量。
          ???? 7、大集合對(duì)象擁有大數(shù)據(jù)量的業(yè)務(wù)對(duì)象的時(shí)候,可以考慮分塊進(jìn)行處理 ,然后解決一塊釋放一塊的策略。
          ???? 8、不要在經(jīng)常調(diào)用的方法中創(chuàng)建對(duì)象 ,尤其是忌諱在循環(huán)中創(chuàng)建對(duì)象。可以適當(dāng)?shù)氖褂胔ashtable,vector 創(chuàng)建一組對(duì)象容器,然后從容器中去取那些對(duì)象,而不用每次new之后又丟棄。
          ???? 9、一般都是發(fā)生在開啟大型文件或跟數(shù)據(jù)庫(kù)一次拿了太多的數(shù)據(jù),造成 Out Of Memory Error 的狀況,這時(shí)就大概要計(jì)算一下數(shù)據(jù)量的最大值是多少,并且設(shè)定所需最小及最大的內(nèi)存空間值。
          ???? 10、盡量少用finalize函數(shù) ,因?yàn)閒inalize()會(huì)加大GC的工作量,而GC相當(dāng)于耗費(fèi)系統(tǒng)的計(jì)算能力。
          ??? 11、不要過(guò)濫使用哈希表 ,有一定開發(fā)經(jīng)驗(yàn)的開發(fā)人員經(jīng)常會(huì)使用hash表(hash表在JDK中的一個(gè)實(shí)現(xiàn)就是HashMap)來(lái)緩存一些數(shù)據(jù),從而提高系統(tǒng)的運(yùn)行速度。比如使用HashMap緩存一些物料信息、人員信息等基礎(chǔ)資料,這在提高系統(tǒng)速度的同時(shí)也加大了系統(tǒng)的內(nèi)存占用,特別是當(dāng)緩存的資料比較多的時(shí)候。其實(shí)我們可以使用操作系統(tǒng)中的緩存的概念來(lái)解決這個(gè)問(wèn)題,也就是給被緩存的分配一個(gè)一定大小的緩存容器,按照一定的算法淘汰不需要繼續(xù)緩存的對(duì)象,這樣一方面會(huì)因?yàn)檫M(jìn)行了對(duì)象緩存而提高了系統(tǒng)的運(yùn)行效率,同時(shí)由于緩存容器不是無(wú)限制擴(kuò)大,從而也減少了系統(tǒng)的內(nèi)存占用?,F(xiàn)在有很多開源的緩存實(shí)現(xiàn)項(xiàng)目,比如ehcache、oscache等,這些項(xiàng)目都實(shí)現(xiàn)了FIFO、MRU等常見的緩存算法

          ?

          關(guān)鍵詞:JAVA?? 內(nèi)存?? 轉(zhuǎn)帖

          posted on 2011-05-18 20:58 jadmin 閱讀(111) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 清徐县| 醴陵市| 长子县| 峨眉山市| 双柏县| 绥棱县| 渝北区| 凤凰县| 安多县| 余干县| 泸西县| 广灵县| 响水县| 泗洪县| 大庆市| 进贤县| 泗水县| 和平县| 海口市| 库车县| 蒙山县| 禄丰县| 宜州市| 德惠市| 台湾省| 泸溪县| 长兴县| 岐山县| 正阳县| 海城市| 云南省| 湖北省| 武安市| 秭归县| 腾冲县| 怀仁县| 图们市| 若尔盖县| 陇西县| 汪清县| 江都市|