posts - 3,  comments - 2,  trackbacks - 0
            2010年2月9日
          private void compressImageFile() {
                  JFileChooser fileChooser = new JFileChooser("d://");
                  fileChooser.showOpenDialog(this);
                  File file = fileChooser.getSelectedFile();
          
                  if (null != file && file.exists()) {
                      Toolkit toolkit = Toolkit.getDefaultToolkit();
                      Image srcImage = toolkit.getImage(file.getAbsolutePath()); // 構(gòu)造Image對象
                      int wideth = -1;
                      int height = -1;
                      boolean flag = true;
                      while (flag) {
                          wideth = srcImage.getWidth(null); // 得到源圖寬
                          height = srcImage.getHeight(null); // 得到源圖長
                          System.out.println("wideth:" + wideth + " height:" + height);
                          if (wideth > 0 && height > 0) { // 因?yàn)?Toolkit.getImage 是異步讀取,如果
                                                          // wideth 和 height
                                                          // 都大于0,表明圖片已經(jīng)加載完畢
                          // imageCanvas.setImage(srcImage);
                              flag = false;
                          } else {
                              try {
                                  Thread.sleep(10);
                              } catch (Exception e) {
                                  e.printStackTrace();
                              }
                          }
                      }
          
                      // 加載完畢,輸出
                      int w = 1024;
                      float s = (float) wideth / 1024.0f;
                      int h = (int) ((float) height / s);
                      BufferedImage bufferedImage = new BufferedImage(w, h,
                              BufferedImage.TYPE_INT_RGB);
                      // bufferedImage.getGraphics().drawImage(srcImage, 0, 0, 1024,
                      // 768, null); // 繪制縮小后的圖
                      boolean flag2 = false;
                      while (!(flag2 = bufferedImage.getGraphics().drawImage(srcImage, 0,
                              0, w, h, this))) {
                          try {
                              Thread.sleep(10);
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      }
          
                      try {
          
                          File outputFile = new File("d://hhh.jpg");
                          if (!outputFile.exists()) {
                              outputFile.createNewFile();
                          }
                          FileOutputStream out = new FileOutputStream(outputFile); // 輸出到文件流
                          JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
                          encoder.encode(bufferedImage); // 近JPEG編碼
                          out.close();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }
          posted @ 2010-02-09 15:37 Mr. Michael.Q 閱讀(216) | 評論 (0)編輯 收藏
            2008年4月27日

          PS: 閱讀之后感觸很深,以前寫程序都沒考慮到這些細(xì)節(jié).好好學(xué)習(xí)一下,為了以后能寫出高質(zhì)量代碼的專業(yè)程序員而努力

          出自:ibm:developerworks中國網(wǎng)站 歐陽辰周欣

          一 問題的提出

          Java的一個重要優(yōu)點(diǎn)就是通過垃圾收集器(Garbage Collection,GC)自動管理內(nèi)存的回收,程序員不需要通過調(diào)用函數(shù)來釋放內(nèi)存。因此,很多程序員認(rèn)為Java不存在內(nèi)存泄漏問題,或者認(rèn)為即使有內(nèi)存泄漏也不是程序的責(zé)任,而是GC或JVM的問題。其實(shí),這種想法是不正確的,因?yàn)镴ava也存在內(nèi)存泄露,但它的表現(xiàn)與C++不同。

          隨著越來越多的服務(wù)器程序采用Java技術(shù),例如JSP,Servlet, EJB等,服務(wù)器程序往往長期運(yùn)行。另外,在很多嵌入式系統(tǒng)中,內(nèi)存的總量非常有限。內(nèi)存泄露問題也就變得十分關(guān)鍵,即使每次運(yùn)行少量泄漏,長期運(yùn)行之后,系統(tǒng)也是面臨崩潰的危險(xiǎn)。

          二 Java是如何管理內(nèi)存

          為了判斷Java中是否有內(nèi)存泄露,我們首先必須了解Java是如何管理內(nèi)存的。Java的內(nèi)存管理就是對象的分配和釋放問題。在Java中,程序員需要通過關(guān)鍵字new為每個對象申請內(nèi)存空間 (基本類型除外),所有的對象都在堆 (Heap)中分配空間。另外,對象的釋放是由GC決定和執(zhí)行的。在Java中,內(nèi)存的分配是由程序完成的,而內(nèi)存的釋放是有GC完成的,這種收支兩條線的方法確實(shí)簡化了程序員的工作。但同時(shí),它也加重了JVM的工作。這也是Java程序運(yùn)行速度較慢的原因之一。因?yàn)椋珿C為了能夠正確釋放對象,GC必須監(jiān)控每一個對象的運(yùn)行狀態(tài),包括對象的申請、引用、被引用、賦值等,GC都需要進(jìn)行監(jiān)控。

          監(jiān)視對象狀態(tài)是為了更加準(zhǔn)確地、及時(shí)地釋放對象,而釋放對象的根本原則就是該對象不再被引用。

          為了更好理解GC的工作原理,我們可以將對象考慮為有向圖的頂點(diǎn),將引用關(guān)系考慮為圖的有向邊,有向邊從引用者指向被引對象。另外,每個線程對象可以作為一個圖的起始頂點(diǎn),例如大多程序從main進(jìn)程開始執(zhí)行,那么該圖就是以main進(jìn)程頂點(diǎn)開始的一棵根樹。在這個有向圖中,根頂點(diǎn)可達(dá)的對象都是有效對象,GC將不回收這些對象。如果某個對象 (連通子圖)與這個根頂點(diǎn)不可達(dá)(注意,該圖為有向圖),那么我們認(rèn)為這個(這些)對象不再被引用,可以被GC回收。

          以下,我們舉一個例子說明如何用有向圖表示內(nèi)存管理。對于程序的每一個時(shí)刻,我們都有一個有向圖表示JVM的內(nèi)存分配情況。以下右圖,就是左邊程序運(yùn)行到第6行的示意圖。

          圖1

          Java使用有向圖的方式進(jìn)行內(nèi)存管理,可以消除引用循環(huán)的問題,例如有三個對象,相互引用,只要它們和根進(jìn)程不可達(dá)的,那么GC也是可以回收它們的。這種方式的優(yōu)點(diǎn)是管理內(nèi)存的精度很高,但是效率較低。另外一種常用的內(nèi)存管理技術(shù)是使用計(jì)數(shù)器,例如COM模型采用計(jì)數(shù)器方式管理構(gòu)件,它與有向圖相比,精度行低(很難處理循環(huán)引用的問題),但執(zhí)行效率很高。

          三 什么是Java中的內(nèi)存泄露

          下面,我們就可以描述什么是內(nèi)存泄漏。在Java中,內(nèi)存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點(diǎn),首先,這些對象是可達(dá)的,即在有向圖中,存在通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java中的內(nèi)存泄漏,這些對象不會被GC所回收,然而它卻占用內(nèi)存。

          在C++中,內(nèi)存泄漏的范圍更大一些。有些對象被分配了內(nèi)存空間,然后卻不可達(dá),由于C++中沒有GC,這些內(nèi)存將永遠(yuǎn)收不回來。在Java中,這些不可達(dá)的對象都由GC負(fù)責(zé)回收,因此程序員不需要考慮這部分的內(nèi)存泄露。

          通過分析,我們得知,對于C++,程序員需要自己管理邊和頂點(diǎn),而對于Java程序員只需要管理邊就可以了(不需要管理頂點(diǎn)的釋放)。通過這種方式,Java提高了編程的效率。

          圖2

          因此,通過以上分析,我們知道在Java中也有內(nèi)存泄漏,但范圍比C++要小一些。因?yàn)镴ava從語言上保證,任何對象都是可達(dá)的,所有的不可達(dá)對象都由GC管理。

          對于程序員來說,GC基本是透明的,不可見的。雖然,我們只有幾個函數(shù)可以訪問GC,例如運(yùn)行GC的函數(shù)System.gc(),但是根據(jù)Java語言規(guī)范定義, 該函數(shù)不保證JVM的垃圾收集器一定會執(zhí)行。因?yàn)椋煌腏VM實(shí)現(xiàn)者可能使用不同的算法管理GC。通常,GC的線程的優(yōu)先級別較低。JVM調(diào)用GC的策略也有很多種,有的是內(nèi)存使用到達(dá)一定程度時(shí),GC才開始工作,也有定時(shí)執(zhí)行的,有的是平緩執(zhí)行GC,有的是中斷式執(zhí)行GC。但通常來說,我們不需要關(guān)心這些。除非在一些特定的場合,GC的執(zhí)行影響應(yīng)用程序的性能,例如對于基于Web的實(shí)時(shí)系統(tǒng),如網(wǎng)絡(luò)游戲等,用戶不希望GC突然中斷應(yīng)用程序執(zhí)行而進(jìn)行垃圾回收,那么我們需要調(diào)整GC的參數(shù),讓GC能夠通過平緩的方式釋放內(nèi)存,例如將垃圾回收分解為一系列的小步驟執(zhí)行,Sun提供的HotSpot JVM就支持這一特性。

          下面給出了一個簡單的內(nèi)存泄露的例子。在這個例子中,我們循環(huán)申請Object對象,并將所申請的對象放入一個Vector中,如果我們僅僅釋放引用本身,那么Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設(shè)置為null。

          Vector v=new Vector(10);
          for (int i=1;i<100; i++)
          {
          Object o=new Object();
          v.add(o);
          o=null;
          }
          //此時(shí),所有的Object對象都沒有被釋放,因?yàn)樽兞縱引用這些對象。

          四 如何檢測內(nèi)存泄漏

          最后一個重要的問題,就是如何檢測Java的內(nèi)存泄漏。目前,我們通常使用一些工具來檢查Java程序的內(nèi)存泄漏問題。市場上已有幾種專業(yè)檢查Java內(nèi)存泄漏的工具,它們的基本工作原理大同小異,都是通過監(jiān)測Java程序運(yùn)行時(shí),所有對象的申請、釋放等動作,將內(nèi)存管理的所有信息進(jìn)行統(tǒng)計(jì)、分析、可視化。開發(fā)人員將根據(jù)這些信息判斷程序是否有內(nèi)存泄漏問題。這些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。

          下面,我們將簡單介紹Optimizeit的基本功能和工作原理。

          Optimizeit Profiler版本4.11支持Application,Applet,Servlet和Romote Application四類應(yīng)用,并且可以支持大多數(shù)類型的JVM,包括SUN JDK系列,IBM的JDK系列,和Jbuilder的JVM等。并且,該軟件是由Java編寫,因此它支持多種操作系統(tǒng)。Optimizeit系列還包括Thread Debugger和Code Coverage兩個工具,分別用于監(jiān)測運(yùn)行時(shí)的線程狀態(tài)和代碼覆蓋面。

          當(dāng)設(shè)置好所有的參數(shù)了,我們就可以在OptimizeIt環(huán)境下運(yùn)行被測程序,在程序運(yùn)行過程中,Optimizeit可以監(jiān)視內(nèi)存的使用曲線(如下圖),包括JVM申請的堆(heap)的大小,和實(shí)際使用的內(nèi)存大小。另外,在運(yùn)行過程中,我們可以隨時(shí)暫停程序的運(yùn)行,甚至強(qiáng)行調(diào)用GC,讓GC進(jìn)行內(nèi)存回收。通過內(nèi)存使用曲線,我們可以整體了解程序使用內(nèi)存的情況。這種監(jiān)測對于長期運(yùn)行的應(yīng)用程序非常有必要,也很容易發(fā)現(xiàn)內(nèi)存泄露。

          圖3

          在運(yùn)行過程中,我們還可以從不同視角觀查內(nèi)存的使用情況,Optimizeit提供了四種方式:

          • 堆視角。 這是一個全面的視角,我們可以了解堆中的所有的對象信息(數(shù)量和種類),并進(jìn)行統(tǒng)計(jì)、排序,過濾。了解相關(guān)對象的變化情況。
          • 方法視角。通過方法視角,我們可以得知每一種類的對象,都分配在哪些方法中,以及它們的數(shù)量。
          • 對象視角。給定一個對象,通過對象視角,我們可以顯示它的所有出引用和入引用對象,我們可以了解這個對象的所有引用關(guān)系。
          • 引用圖。 給定一個根,通過引用圖,我們可以顯示從該頂點(diǎn)出發(fā)的所有出引用。

          在運(yùn)行過程中,我們可以隨時(shí)觀察內(nèi)存的使用情況,通過這種方式,我們可以很快找到那些長期不被釋放,并且不再使用的對象。我們通過檢查這些對象的生存周期,確認(rèn)其是否為內(nèi)存泄露。在實(shí)踐當(dāng)中,尋找內(nèi)存泄露是一件非常麻煩的事情,它需要程序員對整個程序的代碼比較清楚,并且需要豐富的調(diào)試經(jīng)驗(yàn),但是這個過程對于很多關(guān)鍵的Java程序都是十分重要的。

          綜上所述,Java也存在內(nèi)存泄露問題,其原因主要是一些對象雖然不再被使用,但它們?nèi)匀槐灰谩榱私鉀Q這些問題,我們可以通過軟件工具來檢查內(nèi)存泄露,檢查的主要原理就是暴露出所有堆中的對象,讓程序員尋找那些無用但仍被引用的對象。

          相關(guān)資源:

          文章:
          Jim Patrick, Handling memory leaks in Java programs,
          http://www-106.ibm.com/developerWorks/library/j-leaks/index.html
          Ed Lycklama, Does Java Technology Have Memory Leaks?
          http://www.klgroup.com/javaone
          Sun, The Java HotSpot Virtual Machine, Technical White Paper

          軟件:
          Sitraka Software's Jprobe http://www.sitraka.com
          Boland Software's Optimizeit http://optimizeit
          IBM alphaWorks' Jinsight http://www.alphaworks.ibm.com/tech/jinsight

          關(guān)于作者
          歐陽辰,北京大學(xué)計(jì)算機(jī)碩士畢業(yè),98年起開始研究基于java的軟件開發(fā)、測試,參與開發(fā)、測試過多個基于Java的應(yīng)用程序和Web服務(wù)項(xiàng)目。聯(lián)系方式yeekee@sina.com
          周欣,北京大學(xué)計(jì)算機(jī)系在讀博士生,主要研究方向:程序理解、逆向工程及軟件度量,聯(lián)系方式 zhouxin@sei.pku.edu.cn

          posted @ 2008-04-27 12:41 Mr. Michael.Q 閱讀(207) | 評論 (0)編輯 收藏

          線程 java 的一大特性,它可以是給定的指令序列、給定的方法中定義的變量或者一些共享數(shù)據(jù)(類一級的變量)。 在 java 中每個 線程 有自己的堆棧和程序計(jì)數(shù)器(pc),其中堆棧是用來跟蹤 線程 的上下文(上下文是當(dāng) 線程 執(zhí)行到某處時(shí),當(dāng)前的局部變量的值),而程序計(jì)數(shù)器則用來跟蹤當(dāng)前 線程 正在執(zhí)行的指令。 

          在通常情況下,一個 線程 不能訪問另外一個 線程 的堆棧變量,而且這個 線程 必須處于如下狀態(tài)之一: 

          1.排隊(duì)狀態(tài)(ready),在用戶創(chuàng)建了一個 線程 以后,這個 線程 不會立即運(yùn)行。當(dāng) 線程 中的方法start()被調(diào)用時(shí),這個 線程 就會進(jìn)行排隊(duì)狀態(tài),等待調(diào)度程序?qū)⑺D(zhuǎn)入運(yùn)行狀態(tài)(running)。當(dāng)一個進(jìn)程被執(zhí)行后它也可以進(jìn)行排隊(duì)狀態(tài)。如果調(diào)度程序允許的話,通過調(diào)用方法yield()就可以將進(jìn)程放入排隊(duì)狀態(tài)。 

          2.運(yùn)行狀態(tài)(running),當(dāng)調(diào)度程序?qū)pu的運(yùn)行時(shí)間分配給一個 線程 ,這個 線程 就進(jìn)入了運(yùn)行狀態(tài)開始運(yùn)行。 

          3.等待狀態(tài)(waiting),很多原因都可以導(dǎo)致 線程 處于等待狀態(tài),例如 線程 執(zhí)行過程中被暫停,或者是等待i/o請求的完成而進(jìn)入等待狀態(tài)。 

          java 中不同的 線程 具有不同的優(yōu)先級,高優(yōu)先級的 線程 可以安排在低優(yōu)先級 線程 之前完成。如果多個 線程 具有相同的優(yōu)先級, java 會在不同的 線程 之間切換運(yùn)行。一個應(yīng)用程序可以通過使用 線程 中的方法setpriority()來設(shè)置 線程 的優(yōu)先級,使用方法getpriority()來獲得一個 線程 的優(yōu)先級。 

          線程 的生命周期

          一個 線程 的的生命周期可以分成兩階段:生存(alive)周期和死亡(dead)周期,其中生存周期又包括運(yùn)行狀態(tài)(running)和等待狀態(tài)(waiting)。當(dāng)創(chuàng)建一個新 線程 后,這個 線程 就進(jìn)入了排隊(duì)狀態(tài)(ready),當(dāng) 線程 中的方法start()被調(diào)用時(shí), 線程 就進(jìn)入生存周期,這時(shí)它的方法isalive()始終返回真值,直至 線程 進(jìn)入死亡狀態(tài)。 

          線程 的實(shí)現(xiàn)

          有兩種方法可以實(shí)現(xiàn) 線程 ,一種是擴(kuò)展 java .lang.thread類,另一種是通過 java .lang.runnable接口。 

          posted @ 2008-04-27 12:39 Mr. Michael.Q 閱讀(170) | 評論 (0)編輯 收藏

                地址:http://blog.csdn.net/jerryao/archive/2006/07/04/874101.aspx

          一、堆棧(stack)和堆(heap)?

          (1)內(nèi)存分配的策略

            按照編譯原理的觀點(diǎn),程序運(yùn)行時(shí)的內(nèi)存分配有三種策略,分別是靜態(tài)的,棧式的,和堆式的.

           靜態(tài)存儲分配是指在編譯時(shí)就能確定每個數(shù)據(jù)目標(biāo)在運(yùn)行時(shí)刻的存儲空間需求,因而在編譯時(shí)就可以給他們分配固定的內(nèi)存空間.這種分配策略要求程序代碼中不允許有可變數(shù)據(jù)結(jié)構(gòu)(比如可變數(shù)組)的存在,也不允許有嵌套或者遞歸的結(jié)構(gòu)出現(xiàn),因?yàn)樗鼈兌紩?dǎo)致編譯程序無法計(jì)算準(zhǔn)確的存儲空間需求.

           棧式存儲分配也可稱為動態(tài)存儲分配,是由一個類似于堆棧的運(yùn)行棧來實(shí)現(xiàn)的.和靜態(tài)存儲分配相反,在棧式存儲方案中,程序?qū)?shù)據(jù)區(qū)的需求在編譯時(shí)是完全未知的,只有到運(yùn)行的時(shí)候才能夠知道,但是規(guī)定在運(yùn)行中進(jìn)入一個程序模塊時(shí),必須知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能夠?yàn)槠浞峙鋬?nèi)存.和我們在數(shù)據(jù)結(jié)構(gòu)所熟知的棧一樣,棧式存儲分配按照先進(jìn)后出的原則進(jìn)行分配。

           靜態(tài)存儲分配要求在編譯時(shí)能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負(fù)責(zé)在編譯時(shí)或運(yùn)行時(shí)模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配,比如可變長度串和對象實(shí)例.堆由大片的可利用塊或空閑塊組成,堆中的內(nèi)存可以按照任意順序分配和釋放.

          (2)堆和棧的比較

            上面的定義從編譯原理的教材中總結(jié)而來,除靜態(tài)存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態(tài)存儲分配,集中比較堆和棧:

           從堆和棧的功能和作用來通俗的比較, 堆主要用來存放對象的,棧主要是用來執(zhí)行程序的 .而這種不同又主要是由于堆和棧的特點(diǎn)決定的:

             在編程中,例如C/C++中,所有的方法調(diào)用都是通過棧來進(jìn)行的,所有的局部變量,形式參數(shù)都是從棧中分配內(nèi)存空間的。實(shí)際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數(shù)的時(shí)候,修改棧指針就可以把棧中的內(nèi)容銷毀.這樣的模式速度最快,當(dāng)然要用來運(yùn)行程序了.需要注意的是,在分配的時(shí)候,比如為一個即將要調(diào)用的程序模塊分配數(shù)據(jù)區(qū)時(shí),應(yīng)事先知道這個數(shù)據(jù)區(qū)的大小,也就說是雖然分配是在程序運(yùn)行時(shí)進(jìn)行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時(shí)確定的,不是在運(yùn)行時(shí).

             堆是應(yīng)用程序在運(yùn)行的時(shí)候請求操作系統(tǒng)分配給自己內(nèi)存,由于從操作系統(tǒng)管理的內(nèi)存分配,所以在分配和銷毀時(shí)都要占用時(shí)間,因此用堆的效率非常低.但是堆的優(yōu)點(diǎn)在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時(shí)間,因此,用堆保存數(shù)據(jù)時(shí)會得到更大的靈活性。事實(shí)上,面向?qū)ο蟮亩鄳B(tài)性,堆內(nèi)存分配是必不可少的,因?yàn)槎鄳B(tài)變量所需的存儲空間只有在運(yùn)行時(shí)創(chuàng)建了對象之后才能確定.在C++中,要求創(chuàng)建一個對象時(shí),只需用new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時(shí),會在堆里自動進(jìn)行數(shù)據(jù)的保存.當(dāng)然,為達(dá)到這種靈活性,必然會付出一定的代價(jià):在堆里分配存儲空間時(shí)會花掉更長的時(shí)間!這也正是導(dǎo)致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優(yōu)點(diǎn)往往也是人的缺點(diǎn),人的缺點(diǎn)往往也是人的優(yōu)點(diǎn)(暈~).

          (3)JVM中的堆和棧

            JVM是基于堆棧的虛擬機(jī).JVM為每個新創(chuàng)建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運(yùn)行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態(tài)。JVM對堆棧只進(jìn)行兩種操作:以幀為單位的壓棧和出棧操作。

            我們知道,某個線程正在執(zhí)行的方法稱為此線程的當(dāng)前方法.我們可能不知道,當(dāng)前方法使用的幀稱為當(dāng)前幀。當(dāng)線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當(dāng)前幀.在此方法執(zhí)行期間,這個幀將用來保存參數(shù),局部變量,中間計(jì)算過程和其他數(shù)據(jù).這個幀在這里和編譯原理中的活動紀(jì)錄的概念是差不多的.

            從Java的這種分配機(jī)制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統(tǒng)在建立某個進(jìn)程時(shí)或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個線程建立的存儲區(qū)域,該區(qū)域具有先進(jìn)后出的特性。

             每一個Java應(yīng)用都唯一對應(yīng)一個JVM實(shí)例,每一個實(shí)例唯一對應(yīng)一個堆。應(yīng)用程序在運(yùn)行中所創(chuàng)建的所有類實(shí)例或數(shù)組都放在這個堆中,并由應(yīng)用所有的線程共享.跟C/C++不同,Java中分配堆內(nèi)存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時(shí)從兩個地方都分配內(nèi)存,在堆中分配的內(nèi)存實(shí)際建立這個對象,而在堆棧中分配的內(nèi)存只是一個指向這個堆對象的指針(引用)而已。

          posted @ 2008-04-27 12:37 Mr. Michael.Q 閱讀(253) | 評論 (0)編輯 收藏

                地址:http://blog.csdn.net/jerryao/archive/2006/07/04/874101.aspx

          一、堆棧(stack)和堆(heap)?

          (1)內(nèi)存分配的策略

            按照編譯原理的觀點(diǎn),程序運(yùn)行時(shí)的內(nèi)存分配有三種策略,分別是靜態(tài)的,棧式的,和堆式的.

           靜態(tài)存儲分配是指在編譯時(shí)就能確定每個數(shù)據(jù)目標(biāo)在運(yùn)行時(shí)刻的存儲空間需求,因而在編譯時(shí)就可以給他們分配固定的內(nèi)存空間.這種分配策略要求程序代碼中不允許有可變數(shù)據(jù)結(jié)構(gòu)(比如可變數(shù)組)的存在,也不允許有嵌套或者遞歸的結(jié)構(gòu)出現(xiàn),因?yàn)樗鼈兌紩?dǎo)致編譯程序無法計(jì)算準(zhǔn)確的存儲空間需求.

           棧式存儲分配也可稱為動態(tài)存儲分配,是由一個類似于堆棧的運(yùn)行棧來實(shí)現(xiàn)的.和靜態(tài)存儲分配相反,在棧式存儲方案中,程序?qū)?shù)據(jù)區(qū)的需求在編譯時(shí)是完全未知的,只有到運(yùn)行的時(shí)候才能夠知道,但是規(guī)定在運(yùn)行中進(jìn)入一個程序模塊時(shí),必須知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能夠?yàn)槠浞峙鋬?nèi)存.和我們在數(shù)據(jù)結(jié)構(gòu)所熟知的棧一樣,棧式存儲分配按照先進(jìn)后出的原則進(jìn)行分配。

           靜態(tài)存儲分配要求在編譯時(shí)能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負(fù)責(zé)在編譯時(shí)或運(yùn)行時(shí)模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配,比如可變長度串和對象實(shí)例.堆由大片的可利用塊或空閑塊組成,堆中的內(nèi)存可以按照任意順序分配和釋放.

          (2)堆和棧的比較

            上面的定義從編譯原理的教材中總結(jié)而來,除靜態(tài)存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態(tài)存儲分配,集中比較堆和棧:

           從堆和棧的功能和作用來通俗的比較, 堆主要用來存放對象的,棧主要是用來執(zhí)行程序的 .而這種不同又主要是由于堆和棧的特點(diǎn)決定的:

             在編程中,例如C/C++中,所有的方法調(diào)用都是通過棧來進(jìn)行的,所有的局部變量,形式參數(shù)都是從棧中分配內(nèi)存空間的。實(shí)際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數(shù)的時(shí)候,修改棧指針就可以把棧中的內(nèi)容銷毀.這樣的模式速度最快,當(dāng)然要用來運(yùn)行程序了.需要注意的是,在分配的時(shí)候,比如為一個即將要調(diào)用的程序模塊分配數(shù)據(jù)區(qū)時(shí),應(yīng)事先知道這個數(shù)據(jù)區(qū)的大小,也就說是雖然分配是在程序運(yùn)行時(shí)進(jìn)行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時(shí)確定的,不是在運(yùn)行時(shí).

             堆是應(yīng)用程序在運(yùn)行的時(shí)候請求操作系統(tǒng)分配給自己內(nèi)存,由于從操作系統(tǒng)管理的內(nèi)存分配,所以在分配和銷毀時(shí)都要占用時(shí)間,因此用堆的效率非常低.但是堆的優(yōu)點(diǎn)在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時(shí)間,因此,用堆保存數(shù)據(jù)時(shí)會得到更大的靈活性。事實(shí)上,面向?qū)ο蟮亩鄳B(tài)性,堆內(nèi)存分配是必不可少的,因?yàn)槎鄳B(tài)變量所需的存儲空間只有在運(yùn)行時(shí)創(chuàng)建了對象之后才能確定.在C++中,要求創(chuàng)建一個對象時(shí),只需用new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時(shí),會在堆里自動進(jìn)行數(shù)據(jù)的保存.當(dāng)然,為達(dá)到這種靈活性,必然會付出一定的代價(jià):在堆里分配存儲空間時(shí)會花掉更長的時(shí)間!這也正是導(dǎo)致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優(yōu)點(diǎn)往往也是人的缺點(diǎn),人的缺點(diǎn)往往也是人的優(yōu)點(diǎn)(暈~).

          (3)JVM中的堆和棧

            JVM是基于堆棧的虛擬機(jī).JVM為每個新創(chuàng)建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運(yùn)行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態(tài)。JVM對堆棧只進(jìn)行兩種操作:以幀為單位的壓棧和出棧操作。

            我們知道,某個線程正在執(zhí)行的方法稱為此線程的當(dāng)前方法.我們可能不知道,當(dāng)前方法使用的幀稱為當(dāng)前幀。當(dāng)線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當(dāng)前幀.在此方法執(zhí)行期間,這個幀將用來保存參數(shù),局部變量,中間計(jì)算過程和其他數(shù)據(jù).這個幀在這里和編譯原理中的活動紀(jì)錄的概念是差不多的.

            從Java的這種分配機(jī)制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統(tǒng)在建立某個進(jìn)程時(shí)或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個線程建立的存儲區(qū)域,該區(qū)域具有先進(jìn)后出的特性。

             每一個Java應(yīng)用都唯一對應(yīng)一個JVM實(shí)例,每一個實(shí)例唯一對應(yīng)一個堆。應(yīng)用程序在運(yùn)行中所創(chuàng)建的所有類實(shí)例或數(shù)組都放在這個堆中,并由應(yīng)用所有的線程共享.跟C/C++不同,Java中分配堆內(nèi)存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時(shí)從兩個地方都分配內(nèi)存,在堆中分配的內(nèi)存實(shí)際建立這個對象,而在堆棧中分配的內(nèi)存只是一個指向這個堆對象的指針(引用)而已。

          posted @ 2008-04-27 12:37 Mr. Michael.Q 閱讀(2526) | 評論 (0)編輯 收藏

          進(jìn)程通常被定義為一個正在運(yùn)行的程序的實(shí)例,它由兩個部分組成:  
            *   一個組成部分是操作系統(tǒng)用來管理進(jìn)程的內(nèi)核對象。內(nèi)核對象也是系統(tǒng)用來存放關(guān)于進(jìn)程的統(tǒng)計(jì)信息的地方。  
            *   另一個組成部分是地址空間,它包含所有可執(zhí)行模塊或DLL模塊的代碼和數(shù)據(jù)。它還包含動態(tài)內(nèi)存分配的空間。如線程堆棧和堆棧分配空間。  
          進(jìn)程是不活潑的。若要使進(jìn)程完成某項(xiàng)操作,它必須擁有一個在它的環(huán)境中運(yùn)行的線程,該線程負(fù)責(zé)執(zhí)行包含在進(jìn)程的地址空間中的代碼。實(shí)際上,單個進(jìn)程可能包含若干個線程,所有這些線程都“同時(shí)”執(zhí)行進(jìn)程地址空間中的代碼。為此,每個線程都有它自己的一組CPU寄存器和它自己的堆棧。每個進(jìn)程至少擁有一個線程,來執(zhí)行進(jìn)程的地址空間中的代碼。如果沒有線程來執(zhí)行進(jìn)程的地址空間中的代碼,那么進(jìn)程就沒有存在的理由了,系統(tǒng)就將自動撤消該進(jìn)程和它的地址空間。  
            若要使所有這些線程都能運(yùn)行,操作系統(tǒng)就要為每個線程安排一定的CPU時(shí)間。它通過以一種循環(huán)方式為線程提供時(shí)間片(稱為量程),從而造成一種假象,仿佛所有線程都是同時(shí)運(yùn)行的一樣。  
            當(dāng)創(chuàng)建一個進(jìn)程時(shí),系統(tǒng)會自動創(chuàng)建它的第一個線程,稱為主線程。然后,該線程可以創(chuàng)建其他的線程,而這些線程又能創(chuàng)建更多的線程。  
          ---------------------------------------------------------------摘自 《Windows   核心編程》

          進(jìn)程有三大部分:代碼段、數(shù)據(jù)段、PCB(進(jìn)程控制段)。
          操作系統(tǒng)正是通過PCB來管理這多個進(jìn)程。在這樣的系統(tǒng)里,進(jìn)程是操作系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,又是一個可擁有資源的獨(dú)立單位。
          線程:系統(tǒng)調(diào)度和分派的基本單位。
          進(jìn)程和線程有如下不同:
              進(jìn)程可以擁有資源,線程共享進(jìn)程擁有的資源
              進(jìn)程間的切換必須保存PCB
          ---------------------------------------------------------------
          微軟官方對進(jìn)程和線程的定義:  
            進(jìn)程:用最簡潔的話來說,進(jìn)程就是一個正在執(zhí)行的程序,一個或多個線程在進(jìn)程中運(yùn)行,線程是操作系統(tǒng)分配CPU運(yùn)算時(shí)間的最小單位。每一個進(jìn)程都提供了運(yùn)行一個程序所必需的資源,一個進(jìn)程具有4GB的虛擬地址空間(Windows   NT   Server   Enterprise   Edition及Windows   2000   Advanced   Server中低3GB虛擬地址空間供進(jìn)程使用,高1GB供操作系統(tǒng)的內(nèi)核代碼使用。Windows   NT/2000中低2GB供進(jìn)程使用,高2GB供操作系統(tǒng)內(nèi)核代碼使用。Windows9X:0——64K只讀空間用來裝入Microsoft   DOS信息,64K——4M裝入DOS的兼容代碼,4M——2GB的私有空間供進(jìn)程使用,2GB——3GB的共享空間裝入各種DLL代碼,3GB——4GB為共享的系統(tǒng)內(nèi)核代碼空間,其中共享的2GB——4GB的空間是99%的“內(nèi)存無效頁錯誤”、“General   Protect   Error(GPE)”及藍(lán)屏的罪魁禍?zhǔn)住#蓤?zhí)行代碼,數(shù)據(jù),對象句柄,環(huán)境變量,優(yōu)先權(quán)以及設(shè)置最大化最小化的功能。每一個進(jìn)程都從一個主線程開始執(zhí)行,但可以在它所擁有的線程中創(chuàng)建額外的線程。一個進(jìn)程的所有線程共享進(jìn)程的虛擬地址空間和系統(tǒng)資源,一個線程的資源包括線程的機(jī)器寄存器設(shè)置,內(nèi)核堆棧,線程環(huán)境變量和進(jìn)程虛擬地址中的用戶堆棧。
          ---------------------------------------------------------------
          兩者的區(qū)別。根據(jù)定義,進(jìn)程為一個數(shù)據(jù)結(jié)構(gòu)及能在其上進(jìn)行的一次操作,它有兩個基本特征,一個是進(jìn)程是可用有資源的獨(dú)立單位,第二個是進(jìn)程同時(shí)又是一個可以獨(dú)立調(diào)度和分派的基本單位,這兩個基本屬性使之能夠獨(dú)立運(yùn)行,也能夠并發(fā)運(yùn)行。但是在并發(fā)運(yùn)行的時(shí)候,系統(tǒng)還需要執(zhí)行一系列操作:
                    1、需要創(chuàng)建進(jìn)程,并為之分配其所必需的資源。
                    2、撤銷進(jìn)程,對資源進(jìn)行回收。
                    3、進(jìn)程切換,它需要保留當(dāng)前進(jìn)程的CPU環(huán)境和設(shè)置新選中進(jìn)程的CPU環(huán)境,為此需要花費(fèi)不少處理時(shí)間。正因?yàn)檫M(jìn)程擁有資源,所以在并發(fā)執(zhí)行進(jìn)程的時(shí)候,在創(chuàng)建、撤銷和切換種,系統(tǒng)需要付出較大的開銷,因此,系統(tǒng)中設(shè)置的進(jìn)程不能太多,進(jìn)程切換的頻率也不能過高,這就限制了并發(fā)程度的提高。為了解決這一問題,于是產(chǎn)生并引入了線程概念。  
          線程是進(jìn)程中的一個實(shí)體,它的基本思想是將程序的執(zhí)行和資源分開,只擁有一點(diǎn)必不可少的資源。一個進(jìn)程可用有多個線程,但它可以和同屬于同一進(jìn)程的其他線程共享進(jìn)程所擁有的所有的資源,同一進(jìn)程中的線程之間可以并發(fā)執(zhí)行。這樣的話,并發(fā)程度可以獲得顯著的提高。線程也具有許多進(jìn)程所具有的特征,因此被稱為輕型進(jìn)程。
          ---------------------------------------------------------------網(wǎng)上收集

          posted @ 2008-04-27 12:36 Mr. Michael.Q 閱讀(270) | 評論 (0)編輯 收藏
            2008年4月1日

          以前在公司都用SQL Server ,對Oracle的操作不是很熟悉.由于工作需要,我要連接同事的Oracle數(shù)據(jù)庫.于是上網(wǎng)找了方法..

          網(wǎng)上方法很多,據(jù)說有兩種方法可以連接遠(yuǎn)程數(shù)據(jù)庫,一種是使用服務(wù)管理里Oracle Net Manager的的圖形界面操作,這種我嘗試了一下,沒有成功.. 于是 我嘗試第二種. 第二種方法是 直接修改Oracle數(shù)據(jù)庫的文件.我覺得這方法比較簡單.于是在這里記一下.

          操作步驟:

          1)

          打開目錄D:\oracle安裝目錄\ora92\network\admin中的文件tnsnames.ora。

          2) 添加代碼:

          WAREHOUSE =
            (DESCRIPTION =
              (ADDRESS_LIST =
                (ADDRESS = (PROTOCOL = TCP)(HOST=10.1.10.158)(PORT = 1521))
              )
             (CONNECT_DATA =
               (SID = orcl )
             )
          )

          修改說明:

          WAREHOUSE:這名字是你用來連接的名字,隨便取.

          10.1.10.158:遠(yuǎn)程計(jì)算機(jī)的IP地址.

          orcl : 遠(yuǎn)程計(jì)算機(jī)數(shù)據(jù)庫名.

          保存之后就可以了.

          PS:在admin 文件夾中還看到其他的名字,感覺這名字很熟悉,找個時(shí)間也看看這些個文件間的關(guān)聯(lián).應(yīng)該是一件有意思的事情

          posted @ 2008-04-01 12:38 Mr. Michael.Q 閱讀(837) | 評論 (0)編輯 收藏
            2008年3月27日
              最近老遇到Oracle的兩個驅(qū)動選擇,不清楚到底選哪個.. 于是,今天在網(wǎng)上找了一下,在CSDN的論壇中,coolhorse168 在官方網(wǎng)站上找來對這兩驅(qū)動的解釋,特地抄過來寫在我的博客里,以備日后查看.

          ojdbc14.jar
              Classes for use with JDK 1.4.  It contains the JDBC driver
              classes, except classes for NLS support in Oracle Object and
              Collection types.

          classes12.jar
              Classes for use with JDK 1.2 and JDK 1.3.  It contains the
              JDBC driver classes, except classes for NLS support in Oracle
              Object and Collection types.

          The Old oracle.jdbc.driver Package Will Go Away Soon !!!
          --------------------------------------------------------

          Beginning in Oracle 9i, Oracle extensions to JDBC are captured in
          the package oracle.jdbc.  This package contains classes and
          interfaces that specify the Oracle extensions in a manner similar
          to the way the classes and interfaces in java.sql specify the
          public JDBC API.

          The use of the package oracle.jdbc.driver has been deprecated
          since the initial version of 9i.  Your code should use the package
          oracle.jdbc instead.  New features since Oracle 9i are incompatible
          with use of the package oracle.jdbc.driver.  Although we continue
          to support the old package oracle.jdbc.driver in this release to
          provide backwards compatibility, the package will definitely be
          removed in the next major release.  If you still have existing
          applications that use the old oracle.jdbc.driver package, now is the
          time to convert your code.

          All that is required to covert your code is to replace
          "oracle.jdbc.driver" with "oracle.jdbc" in the source and recompile.
          This cannot be done piecewise.  You must convert all classes
          and interfaces that are referenced by an application.
          posted @ 2008-03-27 17:18 Mr. Michael.Q 閱讀(331) | 評論 (1)編輯 收藏
            2008年3月26日
               摘要: Tomcat 6.0 的 JNDI數(shù)據(jù)源 設(shè)置  閱讀全文
          posted @ 2008-03-26 21:27 Mr. Michael.Q 閱讀(3745) | 評論 (1)編輯 收藏
            2007年8月3日

          JDBC高級應(yīng)用

          轉(zhuǎn)自 http://www.blogcn.com/user69/galiasun/index.html


          本來想繼續(xù)談JDBC的高級連結(jié)方式,事務(wù)模式.但發(fā)現(xiàn)關(guān)于大對象存儲有很多人在問,所以
          先來插入一節(jié)關(guān)于大對象存儲的內(nèi)容,然后再接著原來的思路寫下去.

          JDBC的大對象存儲聽起來復(fù)雜,其實(shí)如果你明白了原理以后,就非常簡單,網(wǎng)上有關(guān)這方面的
          教材很少,而SUN的文檔中,我從1.2開始看到一在仍然是錯誤的,不知道寫文檔的人長腦子沒
          有,就那幾行代碼你試試不就知道了,這么多次重抄下來還是錯誤的.


          大對象分類:一般來說,大對象分為:大的文本對象,比如一個很長的文本(請你要注意什么是
          文本文件,什么是二進(jìn)制文件)文件,或者是你定義的一個長字符串,比如你定義了:
          String s = "我們要去吃飯了......................然后睡覺!";
          從吃飯到睡覺中間省略了實(shí)際的10000000000000字,雖然你不會真的定義這么稱的String,但
          有時(shí)會從什么地方得到這樣的String,要寫到數(shù)據(jù)庫中.
          另一種就是大的二進(jìn)制對象,象執(zhí)行文件,圖象文件等,注意,word,excel,ppt這些"帶格式"的文
          檔都應(yīng)該以二進(jìn)制對象存儲.

          一般來說,數(shù)據(jù)庫如果支持大對象存儲,會有這幾種類型的SQL數(shù)據(jù)類型:
          BLOB,CLOCB,NLOB,也有的數(shù)據(jù)數(shù)只有一種BLOB,基本上是這樣的:BLOB用來存放二進(jìn)制文件,而
          CLOB用來存放文本文件,NLOB是對多字節(jié)文本文件支持.假如你的文本文件是純英文的,放在
          BLOB中當(dāng)然可以,也就是說它是以byte格式存儲的,而多字節(jié)是以CHAR格式存儲的.

          同樣對于這幾種類型的文檔,有幾種相對應(yīng)的存取方式:
          setter:
          利用PreparedStatement的setXXX方法,
          setAsciiStream()方法用于寫入一般的文本流.setBinaryStream()方法用于寫入二進(jìn)制流
          而setUnicodeStream()用于寫好UNICODE編碼的文本,與此相對應(yīng)的ResultSet中三個getter方法
          用于取回:getAsciiStream(),getBinaryStream(),getBinaryStream().
          對于文件本身,要把它作為一個流,只要new InputStream(new FileInputStream("文件路徑")
          就可以了,但對于大的String對象,你不會寫入文件再轉(zhuǎn)換成輸入流吧?
          new StringBufferInputStream(String s),記住了.
          JDBC2以后提供了java.sql.BLOB對象,我不建議大家使用它,一是很麻類,二是容易出錯,要先插
          入一個空的BLOB對象,然后再填充它,實(shí)在沒有必要,直接setXXX就行了,我試過,至少mysql,
          oracle,sql server是可以直接set的.
          好了,我們先看一個例子如何寫入文件到數(shù)據(jù)庫:
          數(shù)據(jù)結(jié)構(gòu):
          create table test(
            name varchar(200),
            content BLOB
          );
          File f = new File("a.exe";//先生成File對象是為了取得流的長度.FileInputStram可以直接
                                     //傳入文件路徑
          InputStream in = new InputStream(new FileInputStream(f));
          PreparedStatement ps = conn.prepareStatement("insert into test (?,?)";
          ps.setString(1,"a.exe");
          ps.setBinaryStream(2,in,(int)f.length());
          ps.executeUpdate();
          f的長度一定要做從long到int的轉(zhuǎn)換,SUN的文檔中好幾版都沒有改過來.就這么簡單,當(dāng)然,不同的
          數(shù)據(jù)庫存本身要設(shè)置它允許的最大長度,MYSQL默認(rèn)只能傳1M的文件,要修改參數(shù)原能存更大的文件.
          如果要從數(shù)庫中取得文件:
          PreparedStatement ps = conn.prepareStatement("select * from test where name=?");
          ps.setString(1,"a.exe";
          ResultSet rs = ps.executeQuery();
          if(rs.next()){
           InputStream in = rs.getBinaryStream("content";
          }
          得到in對象后,你可以進(jìn)行任何處理,寫向文件和寫向頁面只是out對象不同而已:
          寫向文件:
          DateOutputStream out = new DateOutputStream(new FileOutputStream("b.exe");
          寫向頁面:
          response.reset();
          response.setContType("類型";
          ServletOutputSreamt out = response.getOutputSream();
          得到out對象后,就可以輸出了:
          byte[] buf = new byte[1024];
          int len = 0;
          while((len = in.read(buf)) >0)
            out.write(buf,0,len);
          in.close();
          out.close();
          對于向頁面輸入,要設(shè)置什么樣的ContType,要看你想如何輸出,如果你想讓對方下載,就設(shè)為
          "application/octet-stream",這樣即使是文本,圖象都會下載而不會在瀏覽器中打開.如果你要想
          在瀏覽器中打開,就要設(shè)置相應(yīng)的類型,還要在容器的配置文件中設(shè)置支持這種文檔類型的輸出,但
          對于很多格式的文件,到底要輸出什么類型,其實(shí)就是HTTP的MIME集,比如圖片:image/gif,當(dāng)然你如
          果你的文件擴(kuò)展名(ext)不確定,你也不要用if(ext.equals("gif")......這樣來判斷,我教你一個
          技巧,我之所以說是技巧,是我沒有在別的地方發(fā)現(xiàn)有人用這種方法,對我來說我是絕對不會把別人的
          方法拿來說是我的技巧的:
          構(gòu)造一個file類型的URL,我們知道URL目前JAVA可以支持HTTP,FTP,MAILTO,FILE,LDAP等,從FILE類型
          的URL就可以得到它的MIME:

          URL u = new URL("file://a.exe";
          String mime = u.openConnection().getContentType();
          這樣你就可以直接response.setContType(mime);而不用一個一個類型判斷了.
          好了,大對象存儲就說到這兒,不同的數(shù)據(jù)仍然和些特殊的規(guī)定,不在此一一列舉了.

          posted @ 2007-08-03 00:40 Mr. Michael.Q 閱讀(301) | 評論 (0)編輯 收藏
          僅列出標(biāo)題  
          主站蜘蛛池模板: 资溪县| 巴南区| 芒康县| 克什克腾旗| 关岭| 镇宁| 庆阳市| 徐汇区| 邹城市| 白银市| 扎鲁特旗| 永福县| 乐昌市| 开远市| 明星| 景洪市| 禹城市| 东至县| 长阳| 灌阳县| 江阴市| 琼结县| 准格尔旗| 尼玛县| 专栏| 伊春市| 武汉市| 深水埗区| 孟州市| 墨玉县| 乌兰察布市| 平谷区| 万荣县| 太和县| 兖州市| 进贤县| 新野县| 新余市| 喀喇沁旗| 宝坻区| 当雄县|