
posted @ 2008-12-16 09:37 追風舞者 閱讀(162) | 評論 (0) | 編輯 收藏
|
|||
轉載自:www.aygfsteel.com/zhaobin
來自 勤勞的蜜蜂的博客: 來自Phrancol的博客:
來自八進制的博客: 來自羅明的博客: 來自yangbutao的博客: 來自erylee的博客: posted @ 2008-12-11 14:40 追風舞者 閱讀(225) | 評論 (0) | 編輯 收藏 剛剛找到的面試題目。自己做了一下,反正挺慘不人睹的。貼出來就想幫幫有需要的人
并且問問為什么是這個結果呢?有的題的答案真的想不到啊~想不到~ 一、判斷題(30分) 1.Java程序里,創建新的類對象用關鍵字new,回收無用的類對象使用關鍵字free。 2.對象可以賦值,只要使用賦值號(等號)即可,相當于生成了一個各屬性與賦值對象相同的新對象。 3.有的類定義時可以不定義構造函數,所以構造函數不是必需的。 4.類及其屬性、方法可以同時有一個以上的修飾符來修飾。 5.Java的屏幕坐標是以像素為單位,容器的左下角被確定為坐標的起點 6.抽象方法必須在抽象類中,所以抽象類中的方法都必須是抽象方法。 7.Final類中的屬性和方法都必須被final修飾符修飾。 8.最終類不能派生子類,最終方法不能被覆蓋。 9.子類要調用父類的方法,必須使用super關鍵字。 10.一個Java類可以有多個父類。 11.如果p是父類Parent的對象,而c是子類Child的對象,則語句c = p是正確的。 12.在java集合中,Vector和HashMap是線程安全的。 13.當一個方法在運行過程中產生一個異常,則這個方法會終止,但是整個程序不一定終止運行。 14.接口是特殊的類,所以接口也可以繼承,子接口將繼承父接口的所有常量和抽象方法。 15.用“+”可以實現字符串的拼接,用- 可以從一個字符串中去除一個字符子串。 二、選擇題(30分) 1、關于垃圾收集的哪些敘述是正確的( ): A.程序開發者必須自己創建一個線程進行內存釋放的工作 B.垃圾收集允許程序開發者明確指定并立即釋放該內存 C.垃圾收集將檢查并釋放不再使用的內存 D.垃圾收集能夠在期望的時間釋放被java對象使用的內存 2、下面的哪些賦值語句是不正確的( ): A.float f=11.1; B.double d=5.3E12; C.double d=3.14159; D.double d=3.14D; 3、下面關于變量及其范圍的陳述哪些是不正確的( ): A.實例變量是類的成員變量 B.實例變量用關鍵字static聲明 C.在方法中定義的局部變量在該方法被執行時創建 D.局部變量在使用前必須被初始化 4、下列關于修飾符混用的說法,錯誤的是( ): A.abstract不能與final并列修飾同一個類 B.abstract類中不可以有private的成員 C.abstract方法必須在abstract類中 D.static方法中能處理非static的屬性 5、容器Panel和Applet缺省使用的布局編輯策略是( ): A、BorderLayout B、FlowLayout C、GridLayout D、CardLayout 6、以下標識符中哪項是不合法的( ): A、 BigMeaninglessName B、$int C、1 st D、$1 7、main方法是Java Application程序執行的入口點,關于main方法的方法頭以下哪項是合法的( ): A、 public static void main() B、 public static void main(String[ ] args) C、 public static int main(String[ ] arg) D、 public void main(String arg[ ]) 8、執行完以下代碼int [ ] x = new int[25];后,以下哪項說明是正確的( ): A、 x[24]為0 B、 x[24]未定義 C、 x[25]為0 D、 x[0]為空 9、以下代碼段執行后的輸出結果為( ): int x=3; int y=10; System.out.println(y%x); A、0 B、1 C、2 D、3 10、以下哪個表達式是不合法的( ): A、String x=”Hello”; int y=9; x+=y; B、String x=”Hello”; int y=9; if(x= =y) { } C、String x=”Hello”; int y=9; x=x+y; D、String x=null; int y=(x!=null)&&(x.length()>0) ? x.length : 0 11、編譯運行以下程序后,關于輸出結果的說明正確的是 ( ): public class Conditional{ public static void main(String args[ ]){ int x=4; System.out.println(“value is “+ ((x>4) ? 99.9 :9)); } } A、 輸出結果為:value is 99.99 B、 輸出結果為:value is 9 C、 輸出結果為:value is 9.0 D、 編譯錯誤 12、以下聲明合法的是( ): A、 default String s; B、 public final static native int w( ) C、 abstract double d; D、 abstract final double hyperbolicCosine( ) 13、關于以下application的說明,正確的是( ): 1. class StaticStuff 2. { 3. static int x=10; 4. static { x+=5;} 5. public static void main(String args[ ]) 6. { 7. System.out.println(“x=” + x); 8. } 9. static { x/=3;} 10. } A、 4行與9行不能通過編譯,因為缺少方法名和返回類型 B、 9行不能通過編譯,因為只能有一個靜態初始化器 C、 編譯通過,執行結果為:x=5 D、編譯通過,執行結果為:x=3 14、關于以下程序代碼的說明正確的是( ): 1.class HasStatic{ 2. private static int x=100; 3. public static void main(String args[ ]){ 4. HasStatic hs1=new HasStatic( ); 5. hs1.x++; 6. HasStatic hs2=new HasStatic( ); 7. hs2.x++; 8. hs1=new HasStatic( ); 9. hs1.x++; 10. HasStatic.x- -; 11. System.out.println(“x=”+x); 12. } 13.} A、5行不能通過編譯,因為引用了私有靜態變量 B、10行不能通過編譯,因為x是私有靜態變量 C、程序通過編譯,輸出結果為:x=103 D、程序通過編譯,輸出結果為:x=102 15、以下選項中循環結構合法的是( ): A、while (int i<7){ i++; System.out.println(“i is “+i); } B、int j=3; while(j){ System.out.println(“ j is “+j); } C、int j=0; for(int k=0; j + k !=10; j++,k++){ System.out.println(“ j is “+ j + “k is”+ k); } D、int j=0; do{ System.out.println( “j is “+j++); if (j = = 3) {continue loop;} }while (j<10); 三、簡答題(40分) 1. 寫出下列程序的運行結果 public class Cat { void mi( ) throws NullPointerException { System.out.println( “Cat mi mi .. “ ); } } public class SmallCat extends Cat {int i=8; void mi( ) throws Exception { System.out.println( “SmallCat mi mi .. “ ); } public static void main( String[] a ) throws Exception { Cat cat = new SmallCat(); cat.mi(); } } 寫出下列程序的運行結果 interface Playable { void play(); } interface Bounceable { void play(); } interface Rollable extends Playable, Bounceable { Ball ball = new Ball("PingPang"); } class Ball implements Rollable { private String name; public String getName() { return name; } public Ball(String name) { this.name = name; } public void play() { ball = new Ball("Football"); System.out.println(ball.getName()); } } 寫出下列程序的運行結果 class Value{ public int i = 15; } public class Test{ public static void main(String argv[]){ Test t = new Test(); t.first(); } public void first(){ int i = 5; Value v = new Value(); v.i = 25; second(v, i); System.out.println(v.i); } public void second(Value v, int i){ i = 0; v.i = 20; Value val = new Value(); v = val; System.out.println(v.i + " " + i); } } 寫出下列程序的運行結果 class MyThread extends Thread{ public void run(){ System.out.println("MyThread: run()"); } public void start(){ System.out.println("MyThread: start()"); } } class MyRunnable implements Runnable{ public void run(){ System.out.println("MyRunnable: run()"); } public void start(){ System.out.println("MyRunnable: start()"); } } public class MyTest { public static void main(String args[]){ MyThread myThread = new MyThread(); MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); myThread.start(); thread.start(); } } posted @ 2008-12-03 09:57 追風舞者 閱讀(398) | 評論 (0) | 編輯 收藏 一、參考資料:
二、基本概念 1、堆(Heap) JVM管理的內存叫堆。在32Bit操作系統上有1.5G-2G的限制,而64Bit的就沒有。 JVM初始分配的內存由-Xms指定,默認是物理內存的1/64但小于1G。 JVM最大分配的內存由-Xmx指定,默認是物理內存的1/4但小于1G。 默認空余堆內存小于40%時,JVM就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。 服務器一般設置-Xms、-Xmx相等以避免在每次GC 后調整堆的大小,所以上面的兩個參數沒啥用。
2.基本收集算法
可見,沒有免費的午餐,無論采用復制還是標記清除算法,自動的東西都要付出很大的性能代價。 3.分代 分代是Java垃圾收集的一大亮點,根據對象的生命周期長短,把堆分為3個代:Young,Old和Permanent,根據不同代的特點采用不同的收集算法,揚長避短也。 Young(Nursery),年輕代。研究表明大部分對象都是朝生暮死,隨生隨滅的。因此所有收集器都為年輕代選擇了復制算法。 Young的默認值為4M,隨堆內存增大,約為1/15,JVM會根據情況動態管理其大小變化。 Young的大小非常非常重要,見“后面暫停時間優先收集器”的論述。 Young里面又分為3個區域,一個Eden,所有新建對象都會存在于該區,兩個Survivor區,用來實施復制算法。每次復制就是將Eden和第一塊Survior的活對象復制到第2塊,然后清空Eden與第一塊Survior。Eden與Survivor的比例由-XX:SurvivorRatio=設置,默認為32。Survivio大了會浪費,小了的話,會使一些年輕對象潛逃到老人區,引起老人區的不安,但這個參數對性能并不重要。 Old(Tenured),年老代。年輕代的對象如果能夠挺過數次收集,就會進入老人區。老人區使用標記整理算法。因為老人區的對象都沒那么容易死的,采用復制算法就要反復的復制對象,很不合算,只好采用標記清理算法,但標記清理算法其實也不輕松,每次都要遍歷區域內所有對象,所以還是沒有免費的午餐啊。 -XX:MaxTenuringThreshold=設置熬過年輕代多少次收集后移入老人區,CMS中默認為0,熬過第一次GC就轉入,可以用-XX:+PrintTenuringDistribution查看。 Permanent,持久代。裝載Class信息等基礎數據,默認64M,如果是類很多很多的服務程序,需要加大其設置-XX:MaxPermSize=,否則它滿了之后會引起fullgc()或Out of Memory。 注意Spring,Hibernate這類喜歡AOP動態生成類的框架需要更多的持久代內存。 4.minor/major collection 每個代滿了之后都會促發collection,(另外Concurrent Low Pause Collector默認在老人區68%的時候促發)。GC用較高的頻率對young進行掃描和回收,這種叫做minor collection。 5.小結 Young -- minor collection -- 復制算法 Old(Tenured) -- major colletion -- 標記清除/標記整理算法
三、收集器 1.古老的串行收集器(Serial Collector) 使用 -XX:+UseSerialGC,策略為年輕代串行復制,年老代串行標記整理。 2.吞吐量優先的并行收集器(Throughput Collector) 使用 -XX:+UseParallelGC ,也是JDK5 -server的默認值。策略為: 所以需要2+的CPU時才會優于串行收集器,適用于后臺處理,科學計算。 可以使用-XX:MaxGCPauseMillis= 和 -XX:GCTimeRatio 來調整GC的時間。 3.暫停時間優先的并發收集器(Concurrent Low Pause Collector-CMS) 前面說了這么多,都是為了這節做鋪墊...... 使用-XX:+UseConcMarkSweepGC,策略為: 3.1 年老代詳述 并行(Parallel)與并發(Concurrent)僅一字之差,并行指多條垃圾收集線程并行,并發指用戶線程與垃圾收集線程并發,程序在繼續運行,而垃圾收集程序運行于另一個個CPU上。 并發收集一開始會很短暫的停止一次所有線程來開始初始標記根對象,然后標記線程與應用線程一起并發運行,最后又很短的暫停一次,多線程并行的重新標記之前可能因為并發而漏掉的對象,然后就開始與應用程序并發的清除過程。可見,最長的兩個遍歷過程都是與應用程序并發執行的,比以前的串行算法改進太多太多了!!! 串行標記清除是等年老代滿了再開始收集的,而并發收集因為要與應用程序一起運行,如果滿了才收集,應用程序就無內存可用,所以系統默認68%滿的時候就開始收集。內存已設得較大,吃內存又沒有這么快的時候,可以用-XX:CMSInitiatingOccupancyFraction=恰當增大該比率。 3.2 年輕代詳述 可惜對年輕代的復制收集,依然必須停止所有應用程序線程,原理如此,只能靠多CPU,多收集線程并發來提高收集速度,但除非你的Server獨占整臺服務器,否則如果服務器上本身還有很多其他線程時,切換起來速度就..... 所以,搞到最后,暫停時間的瓶頸就落在了年輕代的復制算法上。 因此Young的大小設置挺重要的,大點就不用頻繁GC,而且增大GC的間隔后,可以讓多點對象自己死掉而不用復制了。但Young增大時,GC造成的停頓時間攀升得非常恐怖,比如在我的機器上,默認8M的Young,只需要幾毫秒的時間,64M就升到90毫秒,而升到256M時,就要到300毫秒了,峰值還會攀到恐怖的800ms。誰叫復制算法,要等Young滿了才開始收集,開始收集就要停止所有線程呢。 3.3 持久代 可設置-XX:+CMSClassUnloadingEnabled 4.增量(train算法)收集器(Incremental Collector) 已停止維護,–Xincgc選項默認轉為并發收集器。 四、暫停時間顯示 加入下列參數 (請將PrintGC和Details中間的空格去掉,CSDN很怪的認為是禁止字句)
會程序運行過程中將顯示如下輸出 9.211: [GC 9.211: [ParNew: 7994K->0K(8128K), 0.0123935 secs] 427172K->419977K(524224K), 0.0125728 secs] 顯示在程序運行的9.211秒發生了Minor的垃圾收集,前一段數據針對新生區,從7994k整理為0k,新生區總大小為8128k,程序暫停了12ms,而后一段數據針對整個堆。 對于年老代的收集,暫停發生在下面兩個階段,CMS-remark的中斷是17毫秒: [GC [1 CMS-initial-mark: 80168K(196608K)] 81144K(261184K), 0.0059036 secs] [1 CMS-remark: 80168K(196608K)] 82493K(261184K),0.0168943 secs] 再加兩個參數 -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime對暫停時間看得更清晰。 五、真正不停的BEA JRockit 與Sun RTS2.0 Bea的JRockit 5.0 R27 的特色之一是動態決定的垃圾收集策略,用戶可以決定自己關心的是吞吐量,暫停時間還是確定的暫停時間,再由JVM在運行時動態決定、改變改變垃圾收集策略。 最可惜JRockt的license很奇怪,雖然平時使用免費,但這個30ms的選項就需要購買整個Weblogic Real Time Server的license。 其他免費選項,有:
JavaOne2007上有Sun的 Java Real-Time System 2.0 的介紹,RTS2.0基于JDK1.5,在Real-Time Garbage Collctor上又有改進,但還在beta版狀態,只供給OEM,更怪。 六、JDK 6.0的改進 因為JDK5.0在Young較大時的表現還是不夠讓人滿意,又繼續看JDK6.0的改進,結果稍稍失望,不涉及我最頭痛的年輕代復制收集改良。 1.年老代的標識-清除收集,并行執行標識 2.加大了Young區的默認大小 3.System.gc()可以與應用程序并發執行 七、小結 1. JDK5.0/6.0 對于服務器應用,我們使用Concurrent Low Pause Collector,對年輕代,暫停時多線程并行復制收集;對年老代,收集器與應用程序并行標記--整理收集,以達到盡量短的垃圾收集時間。 本著沒有深刻測試前不要胡亂優化的宗旨,命令行屬性只需簡單寫為: ![]() 然后要根據應用的情況,在測試軟件輔助可以下看看有沒有JVM的默認值和自動管理做的不夠的地方可以調整,如-xmn 設Young的大小,-XX:MaxPermSize設持久代大小等。 2. JRockit 6.0 R27.2 但因為JDK5的測試結果實在不能滿意,后來又嘗試了JRockit,總體效果要好些。 ![]() posted @ 2008-12-03 09:51 追風舞者 閱讀(221) | 評論 (0) | 編輯 收藏 1.垃圾收集算法的核心思想 Java語言建立了垃圾收集機制,用以跟蹤正在使用的對象和發現并回收不再使用(引用)的對象。該機制可以有效防范動態內存分配中可能發生的兩個危險:因內存垃圾過多而引發的內存耗盡,以及不恰當的內存釋放所造成的內存非法引用。 垃圾收集算法的核心思想是:對虛擬機可用內存空間,即堆空間中的對象進行識別,如果對象正在被引用,那么稱其為存活對象,反之,如果對象不再被引用,則為垃圾對象,可以回收其占據的空間,用于再分配。垃圾收集算法的選擇和垃圾收集系統參數的合理調節直接影響著系統性能,因此需要開發人員做比較深入的了解。 2.觸發主GC(Garbage Collector)的條件 JVM進行次GC的頻率很高,但因為這種GC占用時間極短,所以對系統產生的影響不大。更值得關注的是主GC的觸發條件,因為它對系統影響很明顯。總的來說,有兩個條件會觸發主GC: ①當應用程序空閑時,即沒有應用線程在運行時,GC會被調用。因為GC在優先級最低的線程中進行,所以當應用忙時,GC線程就不會被調用,但以下條件除外。 ②Java堆內存不足時,GC會被調用。當應用線程在運行,并在運行過程中創建新對象,若這時內存空間不足,JVM就會強制地調用GC線程,以便回收內存用于新的分配。若GC一次之后仍不能滿足內存分配的要求,JVM會再進行兩次GC作進一步的嘗試,若仍無法滿足要求,則 JVM將報“out of memory”的錯誤,Java應用將停止。 由于是否進行主GC由JVM根據系統環境決定,而系統環境在不斷的變化當中,所以主GC的運行具有不確定性,無法預計它何時必然出現,但可以確定的是對一個長期運行的應用來說,其主GC是反復進行的。 3.減少GC開銷的措施 根據上述GC的機制,程序的運行會直接影響系統環境的變化,從而影響GC的觸發。若不針對GC的特點進行設計和編碼,就會出現內存駐留等一系列負面影響。為了避免這些影響,基本的原則就是盡可能地減少垃圾和減少GC過程中的開銷。具體措施包括以下幾個方面: (1)不要顯式調用System.gc() 此函數建議JVM進行主GC,雖然只是建議而非一定,但很多情況下它會觸發主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數。 (2)盡量減少臨時對象的使用 臨時對象在跳出函數調用后,會成為垃圾,少用臨時變量就相當于減少了垃圾的產生,從而延長了出現上述第二個觸發條件出現的時間,減少了主GC的機會。 (3)對象不用時最好顯式置為Null 一般而言,為Null的對象都會被作為垃圾處理,所以將不用的對象顯式地設為Null,有利于GC收集器判定垃圾,從而提高了GC的效率。 (4)盡量使用StringBuffer,而不用String來累加字符串(詳見blog另一篇文章JAVA中String與StringBuffer) 由于String是固定長的字符串對象,累加String對象時,并非在一個String對象中擴增,而是重新創建新的String對象,如Str5=Str1+Str2+Str3+Str4,這條語句執行過程中會產生多個垃圾對象,因為對次作“+”操作時都必須創建新的String對象,但這些過渡對象對系統來說是沒有實際意義的,只會增加更多的垃圾。避免這種情況可以改用StringBuffer來累加字符串,因StringBuffer是可變長的,它在原有基礎上進行擴增,不會產生中間對象。 (5)能用基本類型如Int,Long,就不用Integer,Long對象 基本類型變量占用的內存資源比相應對象占用的少得多,如果沒有必要,最好使用基本變量。 (6)盡量少用靜態對象變量 靜態變量屬于全局變量,不會被GC回收,它們會一直占用內存。 (7)分散對象創建或刪除的時間 集中在短時間內大量創建新對象,特別是大對象,會導致突然需要大量內存,JVM在面臨這種情況時,只能進行主GC,以回收內存或整合內存碎片,從而增加主GC的頻率。集中刪除對象,道理也是一樣的。它使得突然出現了大量的垃圾對象,空閑空間必然減少,從而大大增加了下一次創建新對象時強制主GC的機會。 4.gc與finalize方法 ⑴gc方法請求垃圾回收 使用System.gc()可以不管JVM使用的是哪一種垃圾回收的算法,都可以請求Java的垃圾回收。需要注意的是,調用System.gc()也僅僅是一個請求。JVM接受這個消息后,并不是立即做垃圾回收,而只是對幾個垃圾回收算法做了加權,使垃圾回收操作容易發生,或提早發生,或回收較多而已。 ⑵finalize方法透視垃圾收集器的運行 在JVM垃圾收集器收集一個對象之前 ,一般要求程序調用適當的方法釋放資源,但在沒有明確釋放資源的情況下,Java提供了缺省機制來終止化該對象釋放資源,這個方法就是finalize()。它的原型為: protected void finalize() throws Throwable 在finalize()方法返回之后,對象消失,垃圾收集開始執行。原型中的throws Throwable表示它可以拋出任何類型的異常。 因此,當對象即將被銷毀時,有時需要做一些善后工作。可以把這些操作寫在finalize()方法里。
java 代碼
⑶代碼示例 java 代碼
5.Java 內存泄漏 考慮下面的程序,在ObjStack類中,使用push和pop方法來管理堆棧中的對象。兩個方法中的索引(index)用于指示堆棧中下一個可用位置。push方法存儲對新對象的引用并增加索引值,而pop方法減小索引值并返回堆棧最上面的元素。在main方法中,創建了容量為64的棧,并64次調用push方法向它添加對象,此時index的值為64,隨后又32次調用pop方法,則index的值變為32,出棧意味著在堆棧中的空間應該被收集。但事實上,pop方法只是減小了索引值,堆棧仍然保持著對那些對象的引用。故32個無用對象不會被GC回收,造成了內存滲漏。
java 代碼
6.如何消除內存泄漏 雖然Java虛擬機(JVM)及其垃圾收集器(garbage collector,GC)負責管理大多數的內存任務,Java軟件程序中還是有可能出現內存泄漏。實際上,這在大型項目中是一個常見的問題。避免內存泄漏的第一步是要弄清楚它是如何發生的。本文介紹了編寫Java代碼的一些常見的內存泄漏陷阱,以及編寫不泄漏代碼的一些最佳實踐。一旦發生了內存泄漏,要指出造成泄漏的代碼是非常困難的。因此本文還介紹了一種新工具,用來診斷泄漏并指出根本原因。該工具的開銷非常小,因此可以使用它來尋找處于生產中的系統的內存泄漏。 垃圾收集器的作用 雖然垃圾收集器處理了大多數內存管理問題,從而使編程人員的生活變得更輕松了,但是編程人員還是可能犯錯而導致出現內存問題。簡單地說,GC循環地跟蹤所有來自“根”對象(堆棧對象、靜態對象、JNI句柄指向的對象,諸如此類)的引用,并將所有它所能到達的對象標記為活動的。程序只可以操縱這些對象;其他的對象都被刪除了。因為GC使程序不可能到達已被刪除的對象,這么做就是安全的。 雖然內存管理可以說是自動化的,但是這并不能使編程人員免受思考內存管理問題之苦。例如,分配(以及釋放)內存總會有開銷,雖然這種開銷對編程人員來說是不可見的。創建了太多對象的程序將會比完成同樣的功能而創建的對象卻比較少的程序更慢一些(在其他條件相同的情況下)。 而且,與本文更為密切相關的是,如果忘記“釋放”先前分配的內存,就可能造成內存泄漏。如果程序保留對永遠不再使用的對象的引用,這些對象將會占用并耗盡內存,這是因為自動化的垃圾收集器無法證明這些對象將不再使用。正如我們先前所說的,如果存在一個對對象的引用,對象就被定義為活動的,因此不能刪除。為了確保能回收對象占用的內存,編程人員必須確保該對象不能到達。這通常是通過將對象字段設置為null或者從集合(collection)中移除對象而完成的。但是,注意,當局部變量不再使用時,沒有必要將其顯式地設置為null。對這些變量的引用將隨著方法的退出而自動清除。 概括地說,這就是內存托管語言中的內存泄漏產生的主要原因:保留下來卻永遠不再使用的對象引用。 典型泄漏 既然我們知道了在Java中確實有可能發生內存泄漏,就讓我們來看一些典型的內存泄漏及其原因。 全局集合 在大的應用程序中有某種全局的數據儲存庫是很常見的,例如一個JNDI樹或一個會話表。在這些情況下,必須注意管理儲存庫的大小。必須有某種機制從儲存庫中移除不再需要的數據。 這可能有多種方法,但是最常見的一種是周期性運行的某種清除任務。該任務將驗證儲存庫中的數據,并移除任何不再需要的數據。 另一種管理儲存庫的方法是使用反向鏈接(referrer)計數。然后集合負責統計集合中每個入口的反向鏈接的數目。這要求反向鏈接告訴集合何時會退出入口。當反向鏈接數目為零時,該元素就可以從集合中移除了。 緩存 緩存是一種數據結構,用于快速查找已經執行的操作的結果。因此,如果一個操作執行起來很慢,對于常用的輸入數據,就可以將操作的結果緩存,并在下次調用該操作時使用緩存的數據。 緩存通常都是以動態方式實現的,其中新的結果是在執行時添加到緩存中的。典型的算法是: 檢查結果是否在緩存中,如果在,就返回結果。 如果結果不在緩存中,就進行計算。 將計算出來的結果添加到緩存中,以便以后對該操作的調用可以使用。 該算法的問題(或者說是潛在的內存泄漏)出在最后一步。如果調用該操作時有相當多的不同輸入,就將有相當多的結果存儲在緩存中。很明顯這不是正確的方法。 為了預防這種具有潛在破壞性的設計,程序必須確保對于緩存所使用的內存容量有一個上限。因此,更好的算法是: 檢查結果是否在緩存中,如果在,就返回結果。 如果結果不在緩存中,就進行計算。 如果緩存所占的空間過大,就移除緩存最久的結果。 將計算出來的結果添加到緩存中,以便以后對該操作的調用可以使用。 通過始終移除緩存最久的結果,我們實際上進行了這樣的假設:在將來,比起緩存最久的數據,最近輸入的數據更有可能用到。這通常是一個不錯的假設。 新算法將確保緩存的容量處于預定義的內存范圍之內。確切的范圍可能很難計算,因為緩存中的對象在不斷變化,而且它們的引用包羅萬象。為緩存設置正確的大小是一項非常復雜的任務,需要將所使用的內存容量與檢索數據的速度加以平衡。 解決這個問題的另一種方法是使用java.lang.ref.SoftReference類跟蹤緩存中的對象。這種方法保證這些引用能夠被移除,如果虛擬機的內存用盡而需要更多堆的話。 ClassLoader Java ClassLoader結構的使用為內存泄漏提供了許多可乘之機。正是該結構本身的復雜性使ClassLoader在內存泄漏方面存在如此多的問題。ClassLoader的特別之處在于它不僅涉及“常規”的對象引用,還涉及元對象引用,比如:字段、方法和類。這意味著只要有對字段、方法、類或ClassLoader的對象的引用,ClassLoader就會駐留在JVM中。因為ClassLoader本身可以關聯許多類及其靜態字段,所以就有許多內存被泄漏了。 確定泄漏的位置 通常發生內存泄漏的第一個跡象是:在應用程序中出現了OutOfMemoryError。這通常發生在您最不愿意它發生的生產環境中,此時幾乎不能進行調試。有可能是因為測試環境運行應用程序的方式與生產系統不完全相同,因而導致泄漏只出現在生產中。在這種情況下,需要使用一些開銷較低的工具來監控和查找內存泄漏。還需要能夠無需重啟系統或修改代碼就可以將這些工具連接到正在運行的系統上。可能最重要的是,當進行分析時,需要能夠斷開工具而保持系統不受干擾。 雖然OutOfMemoryError通常都是內存泄漏的信號,但是也有可能應用程序確實正在使用這么多的內存;對于后者,或者必須增加JVM可用的堆的數量,或者對應用程序進行某種更改,使它使用較少的內存。但是,在許多情況下,OutOfMemoryError都是內存泄漏的信號。一種查明方法是不間斷地監控GC的活動,確定內存使用量是否隨著時間增加。如果確實如此,就可能發生了內存泄漏。 posted @ 2008-12-03 09:41 追風舞者 閱讀(1311) | 評論 (1) | 編輯 收藏 public class Search {
/** * 前提條件array數組已排序 */ public static boolean binarySearch(int[] array, int target) { boolean result = false; int bottom = 0; int top = array.length-1; while (bottom <= top) { int mid = (top + bottom) / 2; if (target == array[mid]) { result = true; break; } else if (target < array[mid]) { top = mid - 1; } else if (target > array[mid]) { bottom = mid + 1; } } return result; } public static void main(String[] args) { int [] array = {1,3,5,7,9,10}; boolean result = binarySearch(array, 10); System.out.println(result); } } posted @ 2008-10-10 16:19 追風舞者 閱讀(217) | 評論 (0) | 編輯 收藏 import java.io.BufferedReader;
import java.io.IOException; import java.io.InputStreamReader; public class Hanoi { public static void main(String[] args) throws NumberFormatException, IOException { System.out.print("請輸入盤數:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); move(n, 'A', 'B', 'C'); } /** * 將n個盤子借助b,從 a 移到 c */ public static void move(int n, char a, char b, char c) { if (n == 1) System.out.println("盤 " + n + " 由 " + a + " 移至 " + c); else { move(n - 1, a, c, b); System.out.println("盤 " + n + " 由 " + a + " 移至 " + c); move(n - 1, b, a, c); } } } posted @ 2008-10-10 15:56 追風舞者 閱讀(204) | 評論 (0) | 編輯 收藏 public class Sort {
public static void quickSort(int[] array) { quickSort(array, 0, array.length - 1); } private static void quickSort(int[] array, int low, int high) { if (low < high) { int p = partition(array, low, high); quickSort(array, low, p - 1); quickSort(array, p + 1, high); } } private static int partition(int[] array, int low, int high) { int s = array[high]; int i = low - 1; for (int j = low; j < high; j++) { if (array[j] < s) { i++; swap(array, i, j); } } swap(array, ++i, high); return i; } private static void swap(int[] array, int i, int j) { int temp; temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { int [] array = {2,5,3,7,4}; quickSort(array); for(int i = 0;i<array.length;i++){ System.out.println(array[i]); } } } posted @ 2008-10-10 15:39 追風舞者 閱讀(2061) | 評論 (4) | 編輯 收藏 在Ubuntu 8.04中查看顯卡是否裝好
運行glxinfo | grep rendering 如果顯示”direct rendering: Yes”,則已安裝 1、下載驅動 我下載的就是NVIDIA-Linux-x86_64-173.14.12-pkg2.run這個文件,下載后放到home目錄下吧。 2、刪除原驅動包 sudo apt-get -–purge remove nvidia-glx nvidia-glx-new 然后刪除 /lib/linux-restricted-modules/2.6.22-14-generic/文件夾下面帶有nvidia字樣的內容,注意里面有一個隱藏文件.nvidia-new-installer也要刪掉 這里是內核自帶的驅動 再檢查一下這兩個文件是否存在,/etc/init.d/nvidia-glx /etc/init.d/nvidia-kernel 如果存在,刪除掉。 3、安裝要用到的軟件 sudo apt-get install build-essential pkg-config xserver-xorg-dev linux-headers-`uname -r` 安裝編譯包。 我在安裝的時候提示說沒有lib,驅動安裝時候要安裝Kernel Interface,如果采用自己編譯的方式則要求系統中有libc的源碼。我想一般人都是沒有的,呵呵。這個時候我們可以在安裝驅動之前先自己把這個源碼給安裝好,問題就解決了。如何安裝?呵呵,更簡單,強大的 apt install阿, 運行: sudo apt-get install libc6-dev 一行命令搞定。 4、備份 備份文件是一個好習慣。 sudo cp /etc/default/linux-restricted-modules-common ~/linux-restricted-modules-common.backup sudo cp /etc/X11/xorg.conf ~/xorg.conf.backup 5、禁止系統使用默認的驅動 sudo gedit /etc/default/linux-restricted-modules-common 在最后的雙引號中添加nv nvidia_new,即把文件中的“”,改成“nv nvidia_new” 如果前面第二步刪除完整了其實也可以不用執行這一步。 6、將后面的操作記錄在紙上,因為后面會完全在終端字符界面下操作。 7、停止GDM進程 sudo /etc/init.d/gdm stop 按Ctrl+Alt+F1,登錄后進入第7步。 8、安裝驅動 進入下好的驅動所在目錄 執行:sudo sh ./NVIDIA-Linux-x86_64-173.14.12-pkg2.run 安裝過程中 如果提示有舊驅動,詢問是否刪除舊驅動,選Yes; 如果提示缺少某某模塊(modules),詢問是否上網下載,選no; 如果提示編譯模塊,詢問是否進行編譯,選ok; 如果提示將要修改Xorg.conf,詢問是否允許,選Yes; 接下來就是等待安裝完成。 9、安裝完成就回到終端,重啟GDM sudo /etc/init.d/gdm restart 如果失敗,就重啟機子:sudo shutdown -r now 好了,當看到NV的logo后,才表示安裝成功。 如果不想看NVIDIA的LOGO,可以修改 /etc/X11/xorg.conf 在Section “Device”中添加Option “NoLogo” “True” 如: Section “Device” Identifier “通用顯示卡” Driver “nvidia” Option “NoLogo” “True” EndSection 10.顯示高級設置 如果想進行顯示方面的高級設置,在終端下輸入:nvidia-settings命令。 左邊第二項是設置分辨率(server display configuration),從右邊的resolution中選擇分辨率,再點擊apply, 預覽一下,不好取消就行了。 至此,安裝完畢。 如果御載的話就用這個命令 sh NVIDIA-Linux-x86_64-173.14.12-pkg2.run –uninstall 安裝成功,就這么簡單,一般nvidia 會自動配置好所有~` 重新配置x server: sudo apt-get install pkg-config xserver-xorg-dev3 sudo dpkg-reconfigure xserver-xorg posted @ 2008-09-19 13:23 追風舞者 閱讀(514) | 評論 (0) | 編輯 收藏 |
|||