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()); // 構造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) { // 因為 Toolkit.getImage 是異步讀取,如果 // wideth 和 height // 都大于0,表明圖片已經加載完畢 // 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(); } } }
PS: 閱讀之后感觸很深,以前寫程序都沒考慮到這些細節.好好學習一下,為了以后能寫出高質量代碼的專業程序員而努力
出自:ibm:developerworks中國網站 歐陽辰周欣
Java的一個重要優點就是通過垃圾收集器(Garbage Collection,GC)自動管理內存的回收,程序員不需要通過調用函數來釋放內存。因此,很多程序員認為Java不存在內存泄漏問題,或者認為即使有內存泄漏也不是程序的責任,而是GC或JVM的問題。其實,這種想法是不正確的,因為Java也存在內存泄露,但它的表現與C++不同。
隨著越來越多的服務器程序采用Java技術,例如JSP,Servlet, EJB等,服務器程序往往長期運行。另外,在很多嵌入式系統中,內存的總量非常有限。內存泄露問題也就變得十分關鍵,即使每次運行少量泄漏,長期運行之后,系統也是面臨崩潰的危險。
為了判斷Java中是否有內存泄露,我們首先必須了解Java是如何管理內存的。Java的內存管理就是對象的分配和釋放問題。在Java中,程序員需要通過關鍵字new為每個對象申請內存空間 (基本類型除外),所有的對象都在堆 (Heap)中分配空間。另外,對象的釋放是由GC決定和執行的。在Java中,內存的分配是由程序完成的,而內存的釋放是有GC完成的,這種收支兩條線的方法確實簡化了程序員的工作。但同時,它也加重了JVM的工作。這也是Java程序運行速度較慢的原因之一。因為,GC為了能夠正確釋放對象,GC必須監控每一個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC都需要進行監控。
監視對象狀態是為了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。
為了更好理解GC的工作原理,我們可以將對象考慮為有向圖的頂點,將引用關系考慮為圖的有向邊,有向邊從引用者指向被引對象。另外,每個線程對象可以作為一個圖的起始頂點,例如大多程序從main進程開始執行,那么該圖就是以main進程頂點開始的一棵根樹。在這個有向圖中,根頂點可達的對象都是有效對象,GC將不回收這些對象。如果某個對象 (連通子圖)與這個根頂點不可達(注意,該圖為有向圖),那么我們認為這個(這些)對象不再被引用,可以被GC回收。
以下,我們舉一個例子說明如何用有向圖表示內存管理。對于程序的每一個時刻,我們都有一個有向圖表示JVM的內存分配情況。以下右圖,就是左邊程序運行到第6行的示意圖。
Java使用有向圖的方式進行內存管理,可以消除引用循環的問題,例如有三個對象,相互引用,只要它們和根進程不可達的,那么GC也是可以回收它們的。這種方式的優點是管理內存的精度很高,但是效率較低。另外一種常用的內存管理技術是使用計數器,例如COM模型采用計數器方式管理構件,它與有向圖相比,精度行低(很難處理循環引用的問題),但執行效率很高。
下面,我們就可以描述什么是內存泄漏。在Java中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java中的內存泄漏,這些對象不會被GC所回收,然而它卻占用內存。
在C++中,內存泄漏的范圍更大一些。有些對象被分配了內存空間,然后卻不可達,由于C++中沒有GC,這些內存將永遠收不回來。在Java中,這些不可達的對象都由GC負責回收,因此程序員不需要考慮這部分的內存泄露。
通過分析,我們得知,對于C++,程序員需要自己管理邊和頂點,而對于Java程序員只需要管理邊就可以了(不需要管理頂點的釋放)。通過這種方式,Java提高了編程的效率。
因此,通過以上分析,我們知道在Java中也有內存泄漏,但范圍比C++要小一些。因為Java從語言上保證,任何對象都是可達的,所有的不可達對象都由GC管理。
對于程序員來說,GC基本是透明的,不可見的。雖然,我們只有幾個函數可以訪問GC,例如運行GC的函數System.gc(),但是根據Java語言規范定義, 該函數不保證JVM的垃圾收集器一定會執行。因為,不同的JVM實現者可能使用不同的算法管理GC。通常,GC的線程的優先級別較低。JVM調用GC的策略也有很多種,有的是內存使用到達一定程度時,GC才開始工作,也有定時執行的,有的是平緩執行GC,有的是中斷式執行GC。但通常來說,我們不需要關心這些。除非在一些特定的場合,GC的執行影響應用程序的性能,例如對于基于Web的實時系統,如網絡游戲等,用戶不希望GC突然中斷應用程序執行而進行垃圾回收,那么我們需要調整GC的參數,讓GC能夠通過平緩的方式釋放內存,例如將垃圾回收分解為一系列的小步驟執行,Sun提供的HotSpot JVM就支持這一特性。
下面給出了一個簡單的內存泄露的例子。在這個例子中,我們循環申請Object對象,并將所申請的對象放入一個Vector中,如果我們僅僅釋放引用本身,那么Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設置為null。
Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
Object o=new Object();
v.add(o);
o=null;
}
//此時,所有的Object對象都沒有被釋放,因為變量v引用這些對象。
最后一個重要的問題,就是如何檢測Java的內存泄漏。目前,我們通常使用一些工具來檢查Java程序的內存泄漏問題。市場上已有幾種專業檢查Java內存泄漏的工具,它們的基本工作原理大同小異,都是通過監測Java程序運行時,所有對象的申請、釋放等動作,將內存管理的所有信息進行統計、分析、可視化。開發人員將根據這些信息判斷程序是否有內存泄漏問題。這些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。
下面,我們將簡單介紹Optimizeit的基本功能和工作原理。
Optimizeit Profiler版本4.11支持Application,Applet,Servlet和Romote Application四類應用,并且可以支持大多數類型的JVM,包括SUN JDK系列,IBM的JDK系列,和Jbuilder的JVM等。并且,該軟件是由Java編寫,因此它支持多種操作系統。Optimizeit系列還包括Thread Debugger和Code Coverage兩個工具,分別用于監測運行時的線程狀態和代碼覆蓋面。
當設置好所有的參數了,我們就可以在OptimizeIt環境下運行被測程序,在程序運行過程中,Optimizeit可以監視內存的使用曲線(如下圖),包括JVM申請的堆(heap)的大小,和實際使用的內存大小。另外,在運行過程中,我們可以隨時暫停程序的運行,甚至強行調用GC,讓GC進行內存回收。通過內存使用曲線,我們可以整體了解程序使用內存的情況。這種監測對于長期運行的應用程序非常有必要,也很容易發現內存泄露。
在運行過程中,我們還可以從不同視角觀查內存的使用情況,Optimizeit提供了四種方式:
在運行過程中,我們可以隨時觀察內存的使用情況,通過這種方式,我們可以很快找到那些長期不被釋放,并且不再使用的對象。我們通過檢查這些對象的生存周期,確認其是否為內存泄露。在實踐當中,尋找內存泄露是一件非常麻煩的事情,它需要程序員對整個程序的代碼比較清楚,并且需要豐富的調試經驗,但是這個過程對于很多關鍵的Java程序都是十分重要的。
綜上所述,Java也存在內存泄露問題,其原因主要是一些對象雖然不再被使用,但它們仍然被引用。為了解決這些問題,我們可以通過軟件工具來檢查內存泄露,檢查的主要原理就是暴露出所有堆中的對象,讓程序員尋找那些無用但仍被引用的對象。
文章:
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
關于作者
歐陽辰,北京大學計算機碩士畢業,98年起開始研究基于java的軟件開發、測試,參與開發、測試過多個基于Java的應用程序和Web服務項目。聯系方式yeekee@sina.com
周欣,北京大學計算機系在讀博士生,主要研究方向:程序理解、逆向工程及軟件度量,聯系方式 zhouxin@sei.pku.edu.cn
線程 是 java 的一大特性,它可以是給定的指令序列、給定的方法中定義的變量或者一些共享數據(類一級的變量)。 在 java 中每個 線程 有自己的堆棧和程序計數器(pc),其中堆棧是用來跟蹤 線程 的上下文(上下文是當 線程 執行到某處時,當前的局部變量的值),而程序計數器則用來跟蹤當前 線程 正在執行的指令。
在通常情況下,一個 線程 不能訪問另外一個 線程 的堆棧變量,而且這個 線程 必須處于如下狀態之一:
1.排隊狀態(ready),在用戶創建了一個 線程 以后,這個 線程 不會立即運行。當 線程 中的方法start()被調用時,這個 線程 就會進行排隊狀態,等待調度程序將它轉入運行狀態(running)。當一個進程被執行后它也可以進行排隊狀態。如果調度程序允許的話,通過調用方法yield()就可以將進程放入排隊狀態。
2.運行狀態(running),當調度程序將cpu的運行時間分配給一個 線程 ,這個 線程 就進入了運行狀態開始運行。
3.等待狀態(waiting),很多原因都可以導致 線程 處于等待狀態,例如 線程 執行過程中被暫停,或者是等待i/o請求的完成而進入等待狀態。
在 java 中不同的 線程 具有不同的優先級,高優先級的 線程 可以安排在低優先級 線程 之前完成。如果多個 線程 具有相同的優先級, java 會在不同的 線程 之間切換運行。一個應用程序可以通過使用 線程 中的方法setpriority()來設置 線程 的優先級,使用方法getpriority()來獲得一個 線程 的優先級。
線程 的生命周期
一個 線程 的的生命周期可以分成兩階段:生存(alive)周期和死亡(dead)周期,其中生存周期又包括運行狀態(running)和等待狀態(waiting)。當創建一個新 線程 后,這個 線程 就進入了排隊狀態(ready),當 線程 中的方法start()被調用時, 線程 就進入生存周期,這時它的方法isalive()始終返回真值,直至 線程 進入死亡狀態。
線程 的實現
有兩種方法可以實現 線程 ,一種是擴展 java .lang.thread類,另一種是通過 java .lang.runnable接口。
地址:http://blog.csdn.net/jerryao/archive/2006/07/04/874101.aspx
一、堆棧(stack)和堆(heap)?
(1)內存分配的策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似于堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進后出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.
(2)堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較, 堆主要用來存放對象的,棧主要是用來執行程序的 .而這種不同又主要是由于堆和棧的特點決定的:
在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變量,形式參數都是從棧中分配內存空間的。實際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快,當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由于從操作系統管理的內存分配,所以在分配和銷毀時都要占用時間,因此用堆的效率非常低.但是堆的優點在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變量所需的存儲空間只有在運行時創建了對象之后才能確定.在C++中,要求創建一個對象時,只需用new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).
(3)JVM中的堆和棧
JVM是基于堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變量,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進后出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,并由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。
地址:http://blog.csdn.net/jerryao/archive/2006/07/04/874101.aspx
一、堆棧(stack)和堆(heap)?
(1)內存分配的策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似于堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進后出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.
(2)堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較, 堆主要用來存放對象的,棧主要是用來執行程序的 .而這種不同又主要是由于堆和棧的特點決定的:
在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變量,形式參數都是從棧中分配內存空間的。實際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快,當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由于從操作系統管理的內存分配,所以在分配和銷毀時都要占用時間,因此用堆的效率非常低.但是堆的優點在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變量所需的存儲空間只有在運行時創建了對象之后才能確定.在C++中,要求創建一個對象時,只需用new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).
(3)JVM中的堆和棧
JVM是基于堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變量,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進后出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,并由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。
進程通常被定義為一個正在運行的程序的實例,它由兩個部分組成:
* 一個組成部分是操作系統用來管理進程的內核對象。內核對象也是系統用來存放關于進程的統計信息的地方。
* 另一個組成部分是地址空間,它包含所有可執行模塊或DLL模塊的代碼和數據。它還包含動態內存分配的空間。如線程堆棧和堆棧分配空間。
進程是不活潑的。若要使進程完成某項操作,它必須擁有一個在它的環境中運行的線程,該線程負責執行包含在進程的地址空間中的代碼。實際上,單個進程可能包含若干個線程,所有這些線程都“同時”執行進程地址空間中的代碼。為此,每個線程都有它自己的一組CPU寄存器和它自己的堆棧。每個進程至少擁有一個線程,來執行進程的地址空間中的代碼。如果沒有線程來執行進程的地址空間中的代碼,那么進程就沒有存在的理由了,系統就將自動撤消該進程和它的地址空間。
若要使所有這些線程都能運行,操作系統就要為每個線程安排一定的CPU時間。它通過以一種循環方式為線程提供時間片(稱為量程),從而造成一種假象,仿佛所有線程都是同時運行的一樣。
當創建一個進程時,系統會自動創建它的第一個線程,稱為主線程。然后,該線程可以創建其他的線程,而這些線程又能創建更多的線程。
---------------------------------------------------------------摘自 《Windows 核心編程》
進程有三大部分:代碼段、數據段、PCB(進程控制段)。
操作系統正是通過PCB來管理這多個進程。在這樣的系統里,進程是操作系統獨立調度和分派的基本單位,又是一個可擁有資源的獨立單位。
線程:系統調度和分派的基本單位。
進程和線程有如下不同:
進程可以擁有資源,線程共享進程擁有的資源
進程間的切換必須保存PCB
---------------------------------------------------------------
微軟官方對進程和線程的定義:
進程:用最簡潔的話來說,進程就是一個正在執行的程序,一個或多個線程在進程中運行,線程是操作系統分配CPU運算時間的最小單位。每一個進程都提供了運行一個程序所必需的資源,一個進程具有4GB的虛擬地址空間(Windows NT Server Enterprise Edition及Windows 2000 Advanced Server中低3GB虛擬地址空間供進程使用,高1GB供操作系統的內核代碼使用。Windows NT/2000中低2GB供進程使用,高2GB供操作系統內核代碼使用。Windows9X:0——64K只讀空間用來裝入Microsoft DOS信息,64K——4M裝入DOS的兼容代碼,4M——2GB的私有空間供進程使用,2GB——3GB的共享空間裝入各種DLL代碼,3GB——4GB為共享的系統內核代碼空間,其中共享的2GB——4GB的空間是99%的“內存無效頁錯誤”、“General Protect Error(GPE)”及藍屏的罪魁禍首。),可執行代碼,數據,對象句柄,環境變量,優先權以及設置最大化最小化的功能。每一個進程都從一個主線程開始執行,但可以在它所擁有的線程中創建額外的線程。一個進程的所有線程共享進程的虛擬地址空間和系統資源,一個線程的資源包括線程的機器寄存器設置,內核堆棧,線程環境變量和進程虛擬地址中的用戶堆棧。
---------------------------------------------------------------
兩者的區別。根據定義,進程為一個數據結構及能在其上進行的一次操作,它有兩個基本特征,一個是進程是可用有資源的獨立單位,第二個是進程同時又是一個可以獨立調度和分派的基本單位,這兩個基本屬性使之能夠獨立運行,也能夠并發運行。但是在并發運行的時候,系統還需要執行一系列操作:
1、需要創建進程,并為之分配其所必需的資源。
2、撤銷進程,對資源進行回收。
3、進程切換,它需要保留當前進程的CPU環境和設置新選中進程的CPU環境,為此需要花費不少處理時間。正因為進程擁有資源,所以在并發執行進程的時候,在創建、撤銷和切換種,系統需要付出較大的開銷,因此,系統中設置的進程不能太多,進程切換的頻率也不能過高,這就限制了并發程度的提高。為了解決這一問題,于是產生并引入了線程概念。
線程是進程中的一個實體,它的基本思想是將程序的執行和資源分開,只擁有一點必不可少的資源。一個進程可用有多個線程,但它可以和同屬于同一進程的其他線程共享進程所擁有的所有的資源,同一進程中的線程之間可以并發執行。這樣的話,并發程度可以獲得顯著的提高。線程也具有許多進程所具有的特征,因此被稱為輕型進程。
---------------------------------------------------------------網上收集
以前在公司都用SQL Server ,對Oracle的操作不是很熟悉.由于工作需要,我要連接同事的Oracle數據庫.于是上網找了方法..
網上方法很多,據說有兩種方法可以連接遠程數據庫,一種是使用服務管理里Oracle Net Manager的的圖形界面操作,這種我嘗試了一下,沒有成功.. 于是 我嘗試第二種. 第二種方法是 直接修改Oracle數據庫的文件.我覺得這方法比較簡單.于是在這里記一下.
操作步驟:
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:遠程計算機的IP地址.
orcl : 遠程計算機數據庫名.
保存之后就可以了.
PS:在admin 文件夾中還看到其他的名字,感覺這名字很熟悉,找個時間也看看這些個文件間的關聯.應該是一件有意思的事情
JDBC高級應用
轉自 http://www.blogcn.com/user69/galiasun/index.html
本來想繼續談JDBC的高級連結方式,事務模式.但發現關于大對象存儲有很多人在問,所以
先來插入一節關于大對象存儲的內容,然后再接著原來的思路寫下去.
JDBC的大對象存儲聽起來復雜,其實如果你明白了原理以后,就非常簡單,網上有關這方面的
教材很少,而SUN的文檔中,我從1.2開始看到一在仍然是錯誤的,不知道寫文檔的人長腦子沒
有,就那幾行代碼你試試不就知道了,這么多次重抄下來還是錯誤的.
大對象分類:一般來說,大對象分為:大的文本對象,比如一個很長的文本(請你要注意什么是
文本文件,什么是二進制文件)文件,或者是你定義的一個長字符串,比如你定義了:
String s = "我們要去吃飯了......................然后睡覺!";
從吃飯到睡覺中間省略了實際的10000000000000字,雖然你不會真的定義這么稱的String,但
有時會從什么地方得到這樣的String,要寫到數據庫中.
另一種就是大的二進制對象,象執行文件,圖象文件等,注意,word,excel,ppt這些"帶格式"的文
檔都應該以二進制對象存儲.
一般來說,數據庫如果支持大對象存儲,會有這幾種類型的SQL數據類型:
BLOB,CLOCB,NLOB,也有的數據數只有一種BLOB,基本上是這樣的:BLOB用來存放二進制文件,而
CLOB用來存放文本文件,NLOB是對多字節文本文件支持.假如你的文本文件是純英文的,放在
BLOB中當然可以,也就是說它是以byte格式存儲的,而多字節是以CHAR格式存儲的.
同樣對于這幾種類型的文檔,有幾種相對應的存取方式:
setter:
利用PreparedStatement的setXXX方法,
setAsciiStream()方法用于寫入一般的文本流.setBinaryStream()方法用于寫入二進制流
而setUnicodeStream()用于寫好UNICODE編碼的文本,與此相對應的ResultSet中三個getter方法
用于取回:getAsciiStream(),getBinaryStream(),getBinaryStream().
對于文件本身,要把它作為一個流,只要new InputStream(new FileInputStream("文件路徑")
就可以了,但對于大的String對象,你不會寫入文件再轉換成輸入流吧?
new StringBufferInputStream(String s),記住了.
JDBC2以后提供了java.sql.BLOB對象,我不建議大家使用它,一是很麻類,二是容易出錯,要先插
入一個空的BLOB對象,然后再填充它,實在沒有必要,直接setXXX就行了,我試過,至少mysql,
oracle,sql server是可以直接set的.
好了,我們先看一個例子如何寫入文件到數據庫:
數據結構:
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的轉換,SUN的文檔中好幾版都沒有改過來.就這么簡單,當然,不同的
數據庫存本身要設置它允許的最大長度,MYSQL默認只能傳1M的文件,要修改參數原能存更大的文件.
如果要從數庫中取得文件:
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對象后,你可以進行任何處理,寫向文件和寫向頁面只是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();
對于向頁面輸入,要設置什么樣的ContType,要看你想如何輸出,如果你想讓對方下載,就設為
"application/octet-stream",這樣即使是文本,圖象都會下載而不會在瀏覽器中打開.如果你要想
在瀏覽器中打開,就要設置相應的類型,還要在容器的配置文件中設置支持這種文檔類型的輸出,但
對于很多格式的文件,到底要輸出什么類型,其實就是HTTP的MIME集,比如圖片:image/gif,當然你如
果你的文件擴展名(ext)不確定,你也不要用if(ext.equals("gif")......這樣來判斷,我教你一個
技巧,我之所以說是技巧,是我沒有在別的地方發現有人用這種方法,對我來說我是絕對不會把別人的
方法拿來說是我的技巧的:
構造一個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);而不用一個一個類型判斷了.
好了,大對象存儲就說到這兒,不同的數據仍然和些特殊的規定,不在此一一列舉了.