用socket作長鏈接時,出現了內存溢出的錯誤。搞了4天的時間總算是搞定了。java
現總結下:服務器
1.socket通常分爲短鏈接和長鏈接。網絡
長鏈接是一旦一個客戶端登錄上服務器,其與服務器之間的鏈接就不關閉,無論他們之間進行了多少次交易,直到客戶端退出登錄或網絡出現故障。這種技術在聯機交易系統實現有利於提升效率。
短鏈接是客戶端每發一個請求就與服務器創建一個鏈接,交易完成後關閉鏈接,這種技術實現較長鏈接簡單。
長:connect連上後不斷開, 進行N次收發操做.
短:每次都connect, 完成任務後當即斷開. 下次重連.
通常都是accept後啓動一個線程去處理,該線程中的處理大體以下
短鏈接:
run(){
read //讀取請求包
process //處理
write //應答處理結果
}
長鏈接:
run(){
while(NotEnd){
read
process
write
}
}socket
2. 短鏈接進行一次鏈接作完業務邏輯處理後就關閉鏈接,關閉了socket鏈接也就釋放了socket所佔用的資源,因此不會出現內存溢出的問題。ide
長鏈接通常是鏈接上服務器後,會作一個循環的業務邏輯處理。若是這個時候咱們不得不在循環裏建立對象發送到服務器端作處理而後服務器端(反之亦然),那麼就有可能出現內存溢出的問題。spa
例以下面實現的遠程桌面的程序:線程
服務器端code
- com.hjdf.calis.cvrs.util.CvrsSystem.println(serverPort + " 等待鏈接中......");
- serverSkt.setSoTimeout(10*60*1000);//服務器端的超時時間
- clientSkt = serverSkt.accept();
- com.hjdf.calis.cvrs.util.CvrsSystem.println(serverPort+"與" +
- clientSkt.getInetAddress() + " 創建鏈接");
- clientSkt.setSoTimeout(60*1000);//客戶端的超時時間
- ObjectOutputStream out = new ObjectOutputStream(clientSkt.getOutputStream());
- ObjectInputStream in= new ObjectInputStream(clientSkt.getInputStream());
- ObjectOutputStream pipeout = new ObjectOutputStream(outputstream);
- ObjectInputStream pipein = new ObjectInputStream(inputstream);
- String pipstr="GET";
- if(iswrite){
- pipstr = (String) pipein.readObject();
- if (pipstr != null) {
- out.writeObject(pipstr);
- }
- iswrite =false;
- }else{
- out.writeObject(pipstr);
- out.flush();
- }
- ScreenImageInfo screenInfo= (ScreenImageInfo)in.readObject();
- while(screenInfo!=null){
- pipeout.writeObject(screenInfo);
- if(iswrite){
- pipstr = (String) pipein.readObject();
- if (pipstr != null) {
- out.writeObject(pipstr);
- }
- iswrite =false;
- }else{
- out.writeObject(pipstr);
- out.flush();
- }
- screenInfo= (ScreenImageInfo)in.readObject();
- }
客戶端server
- ObjectInputStream in= new ObjectInputStream(clientSkt.getInputStream());
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Dimension screenSize = toolkit.getScreenSize();
- Rectangle screenRect = new Rectangle(screenSize);
- Robot robot = new Robot();
- ObjectOutputStream out = new ObjectOutputStream(clientSkt.getOutputStream());
- while ( (clientCom = (String) in.readObject()) != null) {
- if (clientCom.equals("GET")) {
- BufferedImage p_w_picpath = robot.createScreenCapture(screenRect);
- ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
- JPEGImageEncoder encoder =
- JPEGCodec.createJPEGEncoder(byteOutStream);
- JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(p_w_picpath);
- param.setQuality(0.5f, false);
- encoder.setJPEGEncodeParam(param);
- encoder.encode(p_w_picpath);
- ScreenImageInfo p_w_picpathInfo =
- new ScreenImageInfo(byteOutStream.toByteArray());
- out.writeObject(p_w_picpathInfo);
- }
- }
上面的客戶端循環的建立對象,服務器端循環的讀出。因爲socket流一直沒有獲得釋放,socket流中會一直持有新建立的ScreenImageInfo對象的引用,這樣java的垃圾回收器不會回收循環建立的對象,致使內存溢出。對象
修改服務器端和客戶端,解決內存溢出:
服務器
- com.hjdf.calis.cvrs.util.CvrsSystem.println(serverPort + " 等待鏈接中......");
- serverSkt.setSoTimeout(10*60*1000);//服務器端的超時時間
- clientSkt = serverSkt.accept();
- com.hjdf.calis.cvrs.util.CvrsSystem.println(serverPort+"與" +
- clientSkt.getInetAddress() + " 創建鏈接");
- clientSkt.setSoTimeout(60*1000);//客戶端的超時時間
- /**
- * 把流在循環裏建立,每次都生成新的對象,垃圾回收器能夠回收之前的ScreenImageInfo對象。
- * 若是流在循環外建立,流中會持有循環建立的ScreenImageInfo的對象的引用,
- * ScreenImageInfo的對象不能被回收,致使內存溢出的錯誤。
- **/
- while(true){
- ObjectInputStream in= new ObjectInputStream(clientSkt.getInputStream());
- ObjectOutputStream pipeout = new ObjectOutputStream(outputstream);
- ScreenImageInfo screenInfo= (ScreenImageInfo)in.readObject();
- if(screenInfo!=null){
- pipeout.writeObject(screenInfo);
- }
- }
客戶端
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Dimension screenSize = toolkit.getScreenSize();
- Rectangle screenRect = new Rectangle(screenSize);
- Robot robot = new Robot();
- while(true){
- ObjectOutputStream out = new ObjectOutputStream(clientSkt.getOutputStream());
- BufferedImage p_w_picpath = robot.createScreenCapture(screenRect);
- ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
- JPEGImageEncoder encoder =
- JPEGCodec.createJPEGEncoder(byteOutStream);
- JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(p_w_picpath);
- param.setQuality(0.5f, false);
- encoder.setJPEGEncodeParam(param);
- encoder.encode(p_w_picpath);
- ScreenImageInfo p_w_picpathInfo =
- new ScreenImageInfo(byteOutStream.toByteArray());
- out.writeObject(p_w_picpathInfo);
- }
修改後的服務器端和客戶端socket,雖然也沒有一直獲得釋放,可是ObjectInputStream in= new ObjectInputStream(clientSkt.getInputStream());和ObjectOutputStream out = new ObjectOutputStream(clientSkt.getOutputStream());在循環中每次都會生成新的ObjectInputStream 和ObjectOutputStream 對象,而且該對象的引用也不會被socket所持有。因此java的垃圾回收器能夠回收這些對象,不會出現內存溢出的問題。