少年阿賓

          那些青春的歲月

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

          #

          //spring-mvc.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns=" xmlns:xsi=" xmlns:context=" xmlns:mvc="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd 
              http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
              http://www.springframework.org/schema/util  <!-- 指定系統(tǒng)尋找controller路徑 -->
           <!-- json 數(shù)據(jù)格式轉(zhuǎn)換
           <mvc:annotation-driven>
            <mvc:message-converters>
             <bean id="jsonConverter" class="com.abin.lee.ssh.util.json.fastjson.FastjsonHttpMessageConverter">
              <property name="supportedMediaTypes" value="application/json" />
              <property name="serializerFeature">
               <list>
                <value>WriteMapNullValue</value>
                <value>QuoteFieldNames</value>
               </list>
              </property>
             </bean>
            </mvc:message-converters>

           </mvc:annotation-driven>
           -->
           <!-- 搜索的包路徑 -->
           <context:component-scan base-package="com.abin.lee.ssh"
            use-default-filters="false">
            <context:include-filter type="annotation"
             expression="org.springframework.stereotype.Controller" />
           </context:component-scan>
           
           <!-- jsp視圖解釋器 -->
           <bean id="jspViewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass"
             value="org.springframework.web.servlet.view.JstlView" />
            <property name="prefix" value="/page/" />
            <property name="suffix" value=".jsp" />
           </bean>
           
          </beans>





          //web.xml
          <?xml version="1.0" encoding="UTF-8"?>
          <web-app xmlns:xsi="
            <display-name>front</display-name>
           
             <!-- spring MVC -->
           <servlet>
            <servlet-name>spring-mvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath*:spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>2</load-on-startup>
           </servlet>
           <servlet-mapping>
            <servlet-name>spring-mvc</servlet-name>
            <url-pattern>/mvc/*</url-pattern>
           </servlet-mapping>
           
           
           
           
            <welcome-file-list>
              <welcome-file>index.html</welcome-file>
              <welcome-file>index.htm</welcome-file>
              <welcome-file>index.jsp</welcome-file>
              <welcome-file>default.html</welcome-file>
              <welcome-file>default.htm</welcome-file>
              <welcome-file>default.jsp</welcome-file>
            </welcome-file-list>
          </web-app>





          //UnivernalController.java

          package com.abin.lee.ssh.controller;

          import java.util.ArrayList;
          import java.util.List;

          import org.springframework.context.annotation.Scope;
          import org.springframework.stereotype.Controller;
          import org.springframework.ui.ModelMap;
          import org.springframework.web.bind.annotation.RequestMapping;

          import com.abin.lee.ssh.entity.UniversalBean;
          @Scope("prototype")
          @Controller
          @RequestMapping("/sky/")
          public class UniversalController {
           
           @RequestMapping("/activity")
           public String activity(ModelMap map){
            List<UniversalBean> list=new ArrayList<UniversalBean>();
            UniversalBean bean=null;
            for(int i=0;i<=5;i++){
             bean=new UniversalBean();
             bean.setId(i);
             bean.setName("abin"+i);
             bean.setImageUrl("
          http://localhost:7700/front/"+i+".jpg");
             list.add(bean);
            }
            map.put("list", list);
            return "mobile/show";
           }

          }




          //show.jsp

          <%@ page language="java" contentType="text/html; charset=UTF-8"
              pageEncoding="UTF-8"%>
          <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<html>
          <head>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          <title>Insert title here</title>
          <%@ taglib prefix="c" uri="
          <%@ taglib uri="</head>

          <script language="javascript" type="text/javascript" src="../script/js/share.js" ></script>
          <script type="text/javascript">

          </script>
          <body>
           <div id="content">
            <c:forEach items="${list}" var="obj">
             <div id="" class="lb">
              <c:choose>
               <c:when test="${empty obj.name}">
                I am Empty
               </c:when>
               <c:when test="${obj.name=='abin1'}">
                ${obj.name} is a boy
               </c:when>
               <c:otherwise>
                ${obj.name } is normally
               </c:otherwise>
              </c:choose>
              <img alt="顯示不出來(lái)" src="${obj.imageUrl }"></img>
             </div>
            
            </c:forEach>
           </div>
          </body>
          </html>




















          http://blog.csdn.net/kelly859/article/details/5827365
          posted @ 2013-07-26 10:48 abin 閱讀(382) | 評(píng)論 (0)編輯 收藏

          window的load事件會(huì)在頁(yè)面中的一切都加載完畢時(shí)觸發(fā),但這個(gè)過(guò)程可能會(huì)因?yàn)橐虞d外部資源過(guò)多而頗費(fèi)周折。而DOMContentLoaded事件則在形成完整的DOM樹(shù)之后就會(huì)觸發(fā),不理會(huì)圖像、JavaScript文件、CSS文件或其他資源是否已經(jīng)下載完畢。與load事件不同,DOMContentLoaded支持在頁(yè)面下載的早期添加事件處理程序,這也就意味著用戶(hù)能夠盡早地與頁(yè)面進(jìn)行交互。

          要處理DOMContentLoaded事件,可以為document或window添加相應(yīng)的事件處理程序(盡管這個(gè)事件會(huì)冒泡到window,但它的目標(biāo)實(shí)際上是document)。來(lái)看下面的例子:


          var EventUtil = {
              addHandler
          : function (element, type, handler) {
                 
          if (element.addEventListener) {
                      element
          .addEventListener(type, handler, false);
                 
          } else if (element.attachEvent) {
                      element
          .attachEvent("on" + type, handler);
                 
          } else {
                      element
          ["on" + type] = handler;
                 
          }
             
          }
          };
          EventUtil.addHandler(document, "DOMContentLoaded", function (event) {
              alert
          ("Content loaded.");
          });



          DOMContentLoaded事件對(duì)象不會(huì)提供任何額外的信息(其target屬性是document)。
          Firefox、Chrome、Safari 3.1及更高的版本和Opera 9及更高版本都支持DOMContentLoaded事件,通常這個(gè)事件既可以添加事件處理程序,也可以執(zhí)行其它DOM操作。這個(gè)事件始終都會(huì)在load事件之前觸發(fā)。

          對(duì)于不支持DOMContLoaded的瀏覽器,我們建議在頁(yè)面加載期間設(shè)置一個(gè)事件為0毫秒的超時(shí)調(diào)用,如下面的例子所示:

          sentTimeout(function () {
             
          //在此添加事件處理程序
          }, 0);

          這段代碼實(shí)際意思是:“在當(dāng)前JavaScript處理完成后立即運(yùn)行這個(gè)函數(shù)。”在頁(yè)面下載和構(gòu)建期間,只有一個(gè)JavaScript處理過(guò)程,因此超時(shí)調(diào)用會(huì)在該過(guò)程結(jié)束時(shí)立即觸發(fā)。至于這個(gè)事件與DOMContentLoaded被觸發(fā)的時(shí)間是否同步,主要還是取決與用戶(hù)使用的瀏覽器頁(yè)面中的其它代碼。為了確保這個(gè)方法有效,必須將其作為頁(yè)面中的第一個(gè)超時(shí)調(diào)用;即便如此,也還是無(wú)法保證在所有瀏覽器中該超時(shí)調(diào)用一定會(huì)遭遇load事件被觸發(fā)。



          posted @ 2013-07-26 09:39 abin 閱讀(357) | 評(píng)論 (0)編輯 收藏

          package com.abin.lee.wechat;
          import java.awt.Color;
          import java.awt.Font;
          import java.awt.Graphics2D;
          import java.awt.image.BufferedImage;
          import java.io.File;
          import java.text.SimpleDateFormat;
          import java.util.HashMap;
          import java.util.Map;
          import javax.imageio.ImageIO;
          import javax.imageio.stream.FileImageOutputStream;
          public class WeChatPhoto {
          public String createzPicture(Map<String,String> request){
          String timeStamp=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new java.util.Date());
          File fileOne = new File("E:\\pament\\wechat\\photo\\"+timeStamp+".png");
          try {
          BufferedImage d = ImageIO.read(new File("E:\\pament\\wechat\\photo\\flight-model.png"));
          Graphics2D graohics =d.createGraphics();
          //寫(xiě)字往圖片上面
          String startPlace=request.get("startPlace");
          String arrivalPlace=request.get("arrivalPlace");
          String flightNo=request.get("flightNo");
          String startTime=request.get("startTime");
          String arrivalTime=request.get("arrivalTime");
          Font font = new Font("粗體", Font.PLAIN, 18);// 添加字體的屬性設(shè)置
          graohics.setColor(Color.BLACK);
          graohics.setFont(font);
          graohics.drawString(flightNo, 300, 20);
          graohics.drawString(startPlace, 30, 100);
          graohics.drawString(arrivalPlace, 555, 100);
          graohics.drawString(startTime, 50, 145);
          graohics.drawString(arrivalTime, 600, 145);
          ImageIO.write(d, "png", new FileImageOutputStream(fileOne));
          } catch (Exception e) {
          e.printStackTrace();
          }
          return timeStamp;
          }
          public static void main(String[] args) {
          WeChatPhoto we=new WeChatPhoto();
          Map<String,String> request=new HashMap<String, String>();
          request.put("startPlace", "北京首都T2");
          request.put("arrivalPlace", "上海虹橋T1");
          request.put("startTime", "08:58");
          request.put("arrivalTime", "10:23");
          request.put("flightNo", "MU5183");
          String timeStamp=we.createzPicture(request);
          System.out.println("timeStamp="+timeStamp);
          }
          }
          posted @ 2013-06-07 17:32 abin 閱讀(376) | 評(píng)論 (0)編輯 收藏

          最近因項(xiàng)目存在內(nèi)存泄漏,故進(jìn)行大規(guī)模的JVM性能調(diào)優(yōu) 現(xiàn)把經(jīng)驗(yàn)做一記錄。

          一、JVM內(nèi)存模型及垃圾收集算法

           1.根據(jù)Java虛擬機(jī)規(guī)范,JVM將內(nèi)存劃分為:

          • New(年輕代)
          • Tenured(年老代)
          • 永久代(Perm)

            其中New和Tenured屬于堆內(nèi)存,堆內(nèi)存會(huì)從JVM啟動(dòng)參數(shù)(-Xmx:3G)指定的內(nèi)存中分配,Perm不屬于堆內(nèi)存,有虛擬機(jī)直接分配,但可以通過(guò)-XX:PermSize -XX:MaxPermSize 等參數(shù)調(diào)整其大小。

          • 年輕代(New):年輕代用來(lái)存放JVM剛分配的Java對(duì)象
          • 年老代(Tenured):年輕代中經(jīng)過(guò)垃圾回收沒(méi)有回收掉的對(duì)象將被Copy到年老代
          • 永久代(Perm):永久代存放Class、Method元信息,其大小跟項(xiàng)目的規(guī)模、類(lèi)、方法的量有關(guān),一般設(shè)置為128M就足夠,設(shè)置原則是預(yù)留30%的空間。

          New又分為幾個(gè)部分:

          • Eden:Eden用來(lái)存放JVM剛分配的對(duì)象
          • Survivor1
          • Survivro2:兩個(gè)Survivor空間一樣大,當(dāng)Eden中的對(duì)象經(jīng)過(guò)垃圾回收沒(méi)有被回收掉時(shí),會(huì)在兩個(gè)Survivor之間來(lái)回Copy,當(dāng)滿(mǎn)足某個(gè)條件,比如Copy次數(shù),就會(huì)被Copy到Tenured。顯然,Survivor只是增加了對(duì)象在年輕代中的逗留時(shí)間,增加了被垃圾回收的可能性。

           2.垃圾回收算法

            垃圾回收算法可以分為三類(lèi),都基于標(biāo)記-清除(復(fù)制)算法:

          • Serial算法(單線程)
          • 并行算法
          • 并發(fā)算法

            JVM會(huì)根據(jù)機(jī)器的硬件配置對(duì)每個(gè)內(nèi)存代選擇適合的回收算法,比如,如果機(jī)器多于1個(gè)核,會(huì)對(duì)年輕代選擇并行算法,關(guān)于選擇細(xì)節(jié)請(qǐng)參考JVM調(diào)優(yōu)文檔。

            稍微解釋下的是,并行算法是用多線程進(jìn)行垃圾回收,回收期間會(huì)暫停程序的執(zhí)行,而并發(fā)算法,也是多線程回收,但期間不停止應(yīng)用執(zhí)行。所以,并發(fā)算法適用于交互性高的一些程序。經(jīng)過(guò)觀察,并發(fā)算法會(huì)減少年輕代的大小,其實(shí)就是使用了一個(gè)大的年老代,這反過(guò)來(lái)跟并行算法相比吞吐量相對(duì)較低。

            還有一個(gè)問(wèn)題是,垃圾回收動(dòng)作何時(shí)執(zhí)行?

          • 當(dāng)年輕代內(nèi)存滿(mǎn)時(shí),會(huì)引發(fā)一次普通GC,該GC僅回收年輕代。需要強(qiáng)調(diào)的時(shí),年輕代滿(mǎn)是指Eden代滿(mǎn),Survivor滿(mǎn)不會(huì)引發(fā)GC
          • 當(dāng)年老代滿(mǎn)時(shí)會(huì)引發(fā)Full GC,F(xiàn)ull GC將會(huì)同時(shí)回收年輕代、年老代
          • 當(dāng)永久代滿(mǎn)時(shí)也會(huì)引發(fā)Full GC,會(huì)導(dǎo)致Class、Method元信息的卸載

            另一個(gè)問(wèn)題是,何時(shí)會(huì)拋出OutOfMemoryException,并不是內(nèi)存被耗空的時(shí)候才拋出

          • JVM98%的時(shí)間都花費(fèi)在內(nèi)存回收
          • 每次回收的內(nèi)存小于2%

            滿(mǎn)足這兩個(gè)條件將觸發(fā)OutOfMemoryException,這將會(huì)留給系統(tǒng)一個(gè)微小的間隙以做一些Down之前的操作,比如手動(dòng)打印Heap Dump。

          二、內(nèi)存泄漏及解決方法

           1.系統(tǒng)崩潰前的一些現(xiàn)象:

            • 每次垃圾回收的時(shí)間越來(lái)越長(zhǎng),由之前的10ms延長(zhǎng)到50ms左右,F(xiàn)ullGC的時(shí)間也有之前的0.5s延長(zhǎng)到4、5s
            • FullGC的次數(shù)越來(lái)越多,最頻繁時(shí)隔不到1分鐘就進(jìn)行一次FullGC
            • 年老代的內(nèi)存越來(lái)越大并且每次FullGC后年老代沒(méi)有內(nèi)存被釋放

               之后系統(tǒng)會(huì)無(wú)法響應(yīng)新的請(qǐng)求,逐漸到達(dá)OutOfMemoryError的臨界值。

               2.生成堆的dump文件

               通過(guò)JMX的MBean生成當(dāng)前的Heap信息,大小為一個(gè)3G(整個(gè)堆的大小)的hprof文件,如果沒(méi)有啟動(dòng)JMX可以通過(guò)Java的jmap命令來(lái)生成該文件。

               3.分析dump文件

               下面要考慮的是如何打開(kāi)這個(gè)3G的堆信息文件,顯然一般的Window系統(tǒng)沒(méi)有這么大的內(nèi)存,必須借助高配置的Linux。當(dāng)然我們可以借助X-Window把Linux上的圖形導(dǎo)入到Window。我們考慮用下面幾種工具打開(kāi)該文件:

              1. Visual VM
              2. IBM HeapAnalyzer
              3. JDK 自帶的Hprof工具

               使用這些工具時(shí)為了確保加載速度,建議設(shè)置最大內(nèi)存為6G。使用后發(fā)現(xiàn),這些工具都無(wú)法直觀地觀察到內(nèi)存泄漏,Visual VM雖能觀察到對(duì)象大小,但看不到調(diào)用堆棧;HeapAnalyzer雖然能看到調(diào)用堆棧,卻無(wú)法正確打開(kāi)一個(gè)3G的文件。因此,我們又選用了Eclipse專(zhuān)門(mén)的靜態(tài)內(nèi)存分析工具:Mat。

               4.分析內(nèi)存泄漏

               通過(guò)Mat我們能清楚地看到,哪些對(duì)象被懷疑為內(nèi)存泄漏,哪些對(duì)象占的空間最大及對(duì)象的調(diào)用關(guān)系。針對(duì)本案,在ThreadLocal中有很多的JbpmContext實(shí)例,經(jīng)過(guò)調(diào)查是JBPM的Context沒(méi)有關(guān)閉所致。

               另,通過(guò)Mat或JMX我們還可以分析線程狀態(tài),可以觀察到線程被阻塞在哪個(gè)對(duì)象上,從而判斷系統(tǒng)的瓶頸。

               5.回歸問(wèn)題

                 Q:為什么崩潰前垃圾回收的時(shí)間越來(lái)越長(zhǎng)?

                 A:根據(jù)內(nèi)存模型和垃圾回收算法,垃圾回收分兩部分:內(nèi)存標(biāo)記、清除(復(fù)制),標(biāo)記部分只要內(nèi)存大小固定時(shí)間是不變的,變的是復(fù)制部分,因?yàn)槊看卫厥斩加幸恍┗厥詹坏舻膬?nèi)存,所以增加了復(fù)制量,導(dǎo)致時(shí)間延長(zhǎng)。所以,垃圾回收的時(shí)間也可以作為判斷內(nèi)存泄漏的依據(jù)

                 Q:為什么Full GC的次數(shù)越來(lái)越多?

                 A:因此內(nèi)存的積累,逐漸耗盡了年老代的內(nèi)存,導(dǎo)致新對(duì)象分配沒(méi)有更多的空間,從而導(dǎo)致頻繁的垃圾回收

                 Q:為什么年老代占用的內(nèi)存越來(lái)越大?

                 A:因?yàn)槟贻p代的內(nèi)存無(wú)法被回收,越來(lái)越多地被Copy到年老代

              三、性能調(diào)優(yōu)

               除了上述內(nèi)存泄漏外,我們還發(fā)現(xiàn)CPU長(zhǎng)期不足3%,系統(tǒng)吞吐量不夠,針對(duì)8core×16G、64bit的Linux服務(wù)器來(lái)說(shuō),是嚴(yán)重的資源浪費(fèi)。

               在CPU負(fù)載不足的同時(shí),偶爾會(huì)有用戶(hù)反映請(qǐng)求的時(shí)間過(guò)長(zhǎng),我們意識(shí)到必須對(duì)程序及JVM進(jìn)行調(diào)優(yōu)。從以下幾個(gè)方面進(jìn)行:

              • 線程池:解決用戶(hù)響應(yīng)時(shí)間長(zhǎng)的問(wèn)題
              • 連接池
              • JVM啟動(dòng)參數(shù):調(diào)整各代的內(nèi)存比例和垃圾回收算法,提高吞吐量
              • 程序算法:改進(jìn)程序邏輯算法提高性能

                1.Java線程池(java.util.concurrent.ThreadPoolExecutor)

                  大多數(shù)JVM6上的應(yīng)用采用的線程池都是JDK自帶的線程池,之所以把成熟的Java線程池進(jìn)行羅嗦說(shuō)明,是因?yàn)樵摼€程池的行為與我們想象的有點(diǎn)出入。Java線程池有幾個(gè)重要的配置參數(shù):

              • corePoolSize:核心線程數(shù)(最新線程數(shù))
              • maximumPoolSize:最大線程數(shù),超過(guò)這個(gè)數(shù)量的任務(wù)會(huì)被拒絕,用戶(hù)可以通過(guò)RejectedExecutionHandler接口自定義處理方式
              • keepAliveTime:線程保持活動(dòng)的時(shí)間
              • workQueue:工作隊(duì)列,存放執(zhí)行的任務(wù)

                  Java線程池需要傳入一個(gè)Queue參數(shù)(workQueue)用來(lái)存放執(zhí)行的任務(wù),而對(duì)Queue的不同選擇,線程池有完全不同的行為:

              • SynchronousQueue: 一個(gè)無(wú)容量的等待隊(duì)列,一個(gè)線程的insert操作必須等待另一線程的remove操作,采用這個(gè)Queue線程池將會(huì)為每個(gè)任務(wù)分配一個(gè)新線程
              • LinkedBlockingQueue 無(wú)界隊(duì)列,采用該Queue,線程池將忽略 maximumPoolSize參數(shù),僅用corePoolSize的線程處理所有的任務(wù),未處理的任務(wù)便在LinkedBlockingQueue中排隊(duì)
              • ArrayBlockingQueue: 有界隊(duì)列,在有界隊(duì)列和 maximumPoolSize的作用下,程序?qū)⒑茈y被調(diào)優(yōu):更大的Queue和小的maximumPoolSize將導(dǎo)致CPU的低負(fù)載;小的Queue和大的池,Queue就沒(méi)起動(dòng)應(yīng)有的作用。

                  其實(shí)我們的要求很簡(jiǎn)單,希望線程池能跟連接池一樣,能設(shè)置最小線程數(shù)、最大線程數(shù),當(dāng)最小數(shù)<任務(wù)<最大數(shù)時(shí),應(yīng)該分配新的線程處理;當(dāng)任務(wù)>最大數(shù)時(shí),應(yīng)該等待有空閑線程再處理該任務(wù)。

                  但線程池的設(shè)計(jì)思路是,任務(wù)應(yīng)該放到Queue中,當(dāng)Queue放不下時(shí)再考慮用新線程處理,如果Queue滿(mǎn)且無(wú)法派生新線程,就拒絕該任務(wù)。設(shè)計(jì)導(dǎo)致“先放等執(zhí)行”、“放不下再執(zhí)行”、“拒絕不等待”。所以,根據(jù)不同的Queue參數(shù),要提高吞吐量不能一味地增大maximumPoolSize。

                  當(dāng)然,要達(dá)到我們的目標(biāo),必須對(duì)線程池進(jìn)行一定的封裝,幸運(yùn)的是ThreadPoolExecutor中留了足夠的自定義接口以幫助我們達(dá)到目標(biāo)。我們封裝的方式是:

              • 以SynchronousQueue作為參數(shù),使maximumPoolSize發(fā)揮作用,以防止線程被無(wú)限制的分配,同時(shí)可以通過(guò)提高maximumPoolSize來(lái)提高系統(tǒng)吞吐量
              • 自定義一個(gè)RejectedExecutionHandler,當(dāng)線程數(shù)超過(guò)maximumPoolSize時(shí)進(jìn)行處理,處理方式為隔一段時(shí)間檢查線程池是否可以執(zhí)行新Task,如果可以把拒絕的Task重新放入到線程池,檢查的時(shí)間依賴(lài)keepAliveTime的大小。

                2.連接池(org.apache.commons.dbcp.BasicDataSource)

                  在使用org.apache.commons.dbcp.BasicDataSource的時(shí)候,因?yàn)橹安捎昧四J(rèn)配置,所以當(dāng)訪問(wèn)量大時(shí),通過(guò)JMX觀察到很多Tomcat線程都阻塞在BasicDataSource使用的Apache ObjectPool的鎖上,直接原因當(dāng)時(shí)是因?yàn)锽asicDataSource連接池的最大連接數(shù)設(shè)置的太小,默認(rèn)的BasicDataSource配置,僅使用8個(gè)最大連接。

                  我還觀察到一個(gè)問(wèn)題,當(dāng)較長(zhǎng)的時(shí)間不訪問(wèn)系統(tǒng),比如2天,DB上的Mysql會(huì)斷掉所以的連接,導(dǎo)致連接池中緩存的連接不能用。為了解決這些問(wèn)題,我們充分研究了BasicDataSource,發(fā)現(xiàn)了一些優(yōu)化的點(diǎn):

              • Mysql默認(rèn)支持100個(gè)鏈接,所以每個(gè)連接池的配置要根據(jù)集群中的機(jī)器數(shù)進(jìn)行,如有2臺(tái)服務(wù)器,可每個(gè)設(shè)置為60
              • initialSize:參數(shù)是一直打開(kāi)的連接數(shù)
              • minEvictableIdleTimeMillis:該參數(shù)設(shè)置每個(gè)連接的空閑時(shí)間,超過(guò)這個(gè)時(shí)間連接將被關(guān)閉
              • timeBetweenEvictionRunsMillis:后臺(tái)線程的運(yùn)行周期,用來(lái)檢測(cè)過(guò)期連接
              • maxActive:最大能分配的連接數(shù)
              • maxIdle:最大空閑數(shù),當(dāng)連接使用完畢后發(fā)現(xiàn)連接數(shù)大于maxIdle,連接將被直接關(guān)閉。只有initialSize < x < maxIdle的連接將被定期檢測(cè)是否超期。這個(gè)參數(shù)主要用來(lái)在峰值訪問(wèn)時(shí)提高吞吐量。
              • initialSize是如何保持的?經(jīng)過(guò)研究代碼發(fā)現(xiàn),BasicDataSource會(huì)關(guān)閉所有超期的連接,然后再打開(kāi)initialSize數(shù)量的連接,這個(gè)特性與minEvictableIdleTimeMillis、timeBetweenEvictionRunsMillis一起保證了所有超期的initialSize連接都會(huì)被重新連接,從而避免了Mysql長(zhǎng)時(shí)間無(wú)動(dòng)作會(huì)斷掉連接的問(wèn)題。

                3.JVM參數(shù)

                  在JVM啟動(dòng)參數(shù)中,可以設(shè)置跟內(nèi)存、垃圾回收相關(guān)的一些參數(shù)設(shè)置,默認(rèn)情況不做任何設(shè)置JVM會(huì)工作的很好,但對(duì)一些配置很好的Server和具體的應(yīng)用必須仔細(xì)調(diào)優(yōu)才能獲得最佳性能。通過(guò)設(shè)置我們希望達(dá)到一些目標(biāo):

              • GC的時(shí)間足夠的小
              • GC的次數(shù)足夠的少
              • 發(fā)生Full GC的周期足夠的長(zhǎng)

                前兩個(gè)目前是相悖的,要想GC時(shí)間小必須要一個(gè)更小的堆,要保證GC次數(shù)足夠少,必須保證一個(gè)更大的堆,我們只能取其平衡。

                 (1)針對(duì)JVM堆的設(shè)置一般,可以通過(guò)-Xms -Xmx限定其最小、最大值,為了防止垃圾收集器在最小、最大之間收縮堆而產(chǎn)生額外的時(shí)間,我們通常把最大、最小設(shè)置為相同的值
                 (2)年輕代和年老代將根據(jù)默認(rèn)的比例(1:2)分配堆內(nèi)存,可以通過(guò)調(diào)整二者之間的比率NewRadio來(lái)調(diào)整二者之間的大小,也可以針對(duì)回收代,比如年輕代,通過(guò) -XX:newSize -XX:MaxNewSize來(lái)設(shè)置其絕對(duì)大小。同樣,為了防止年輕代的堆收縮,我們通常會(huì)把-XX:newSize -XX:MaxNewSize設(shè)置為同樣大小

                 (3)年輕代和年老代設(shè)置多大才算合理?這個(gè)我問(wèn)題毫無(wú)疑問(wèn)是沒(méi)有答案的,否則也就不會(huì)有調(diào)優(yōu)。我們觀察一下二者大小變化有哪些影響

              • 更大的年輕代必然導(dǎo)致更小的年老代,大的年輕代會(huì)延長(zhǎng)普通GC的周期,但會(huì)增加每次GC的時(shí)間;小的年老代會(huì)導(dǎo)致更頻繁的Full GC
              • 更小的年輕代必然導(dǎo)致更大年老代,小的年輕代會(huì)導(dǎo)致普通GC很頻繁,但每次的GC時(shí)間會(huì)更短;大的年老代會(huì)減少Full GC的頻率
              • 如何選擇應(yīng)該依賴(lài)應(yīng)用程序?qū)ο笊芷诘姆植记闆r:如果應(yīng)用存在大量的臨時(shí)對(duì)象,應(yīng)該選擇更大的年輕代;如果存在相對(duì)較多的持久對(duì)象,年老代應(yīng)該適當(dāng)增大。但很多應(yīng)用都沒(méi)有這樣明顯的特性,在抉擇時(shí)應(yīng)該根據(jù)以下兩點(diǎn):(A)本著Full GC盡量少的原則,讓年老代盡量緩存常用對(duì)象,JVM的默認(rèn)比例1:2也是這個(gè)道理 (B)通過(guò)觀察應(yīng)用一段時(shí)間,看其他在峰值時(shí)年老代會(huì)占多少內(nèi)存,在不影響Full GC的前提下,根據(jù)實(shí)際情況加大年輕代,比如可以把比例控制在1:1。但應(yīng)該給年老代至少預(yù)留1/3的增長(zhǎng)空間

                (4)在配置較好的機(jī)器上(比如多核、大內(nèi)存),可以為年老代選擇并行收集算法: -XX:+UseParallelOldGC ,默認(rèn)為Serial收集

                (5)線程堆棧的設(shè)置:每個(gè)線程默認(rèn)會(huì)開(kāi)啟1M的堆棧,用于存放棧幀、調(diào)用參數(shù)、局部變量等,對(duì)大多數(shù)應(yīng)用而言這個(gè)默認(rèn)值太了,一般256K就足用。理論上,在內(nèi)存不變的情況下,減少每個(gè)線程的堆棧,可以產(chǎn)生更多的線程,但這實(shí)際上還受限于操作系統(tǒng)。

                (4)可以通過(guò)下面的參數(shù)打Heap Dump信息

              • -XX:HeapDumpPath
              • -XX:+PrintGCDetails
              • -XX:+PrintGCTimeStamps
              • -Xloggc:/usr/aaa/dump/heap_trace.txt

                  通過(guò)下面參數(shù)可以控制OutOfMemoryError時(shí)打印堆的信息

              • -XX:+HeapDumpOnOutOfMemoryError

               請(qǐng)看一下一個(gè)時(shí)間的Java參數(shù)配置:(服務(wù)器:Linux 64Bit,8Core×16G)

               JAVA_OPTS="$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/aaa/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize=1G -XX:MaxNewSize=1G"

              經(jīng)過(guò)觀察該配置非常穩(wěn)定,每次普通GC的時(shí)間在10ms左右,F(xiàn)ull GC基本不發(fā)生,或隔很長(zhǎng)很長(zhǎng)的時(shí)間才發(fā)生一次

              通過(guò)分析dump文件可以發(fā)現(xiàn),每個(gè)1小時(shí)都會(huì)發(fā)生一次Full GC,經(jīng)過(guò)多方求證,只要在JVM中開(kāi)啟了JMX服務(wù),JMX將會(huì)1小時(shí)執(zhí)行一次Full GC以清除引用,關(guān)于這點(diǎn)請(qǐng)參考附件文檔。

               4.程序算法調(diào)優(yōu):本次不作為重點(diǎn)

              參考資料:

              http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

              posted @ 2013-05-31 15:49 abin 閱讀(535) | 評(píng)論 (0)編輯 收藏

              AOP有三種植入切面的方法:其一是編譯期織入,這要求使用特殊的Java編譯器,AspectJ是其中的代表者;其二是類(lèi)裝載期織入,而這要求使用特殊的類(lèi)裝載器,AspectJ和AspectWerkz是其中的代表者;其三為動(dòng)態(tài)代理織入,在運(yùn)行期為目標(biāo)類(lèi)添加增強(qiáng)生成子類(lèi)的方式,Spring AOP采用動(dòng)態(tài)代理織入切面。

              Spring AOP使用了兩種代理機(jī)制,一種是基于JDK的動(dòng)態(tài)代理,另一種是基于CGLib的動(dòng)態(tài)代理,之所以需要兩種代理機(jī)制,很大程度上是因?yàn)镴DK本身只提供基于接口的代理,不支持類(lèi)的代理。

              Spring的三種注入方式:
              接口注入(不推薦)
              getter,setter方式注入(比較常用)
              構(gòu)造器注入(死的應(yīng)用)

              什么是AOP?
              面向切面編程(AOP)完善spring的依賴(lài)注入(DI),面向切面編程在spring中主要表現(xiàn)為兩個(gè)方面
              1.面向切面編程提供聲明式事務(wù)管理
              2.spring支持用戶(hù)自定義的切面

              面向切面編程(aop)是對(duì)面向?qū)ο缶幊蹋╫op)的補(bǔ)充,
              面向?qū)ο缶幊虒⒊绦蚍纸獬筛鱾€(gè)層次的對(duì)象,面向切面編程將程序運(yùn)行過(guò)程分解成各個(gè)切面。
              AOP從程序運(yùn)行角度考慮程序的結(jié)構(gòu),提取業(yè)務(wù)處理過(guò)程的切面,oop是靜態(tài)的抽象,aop是動(dòng)態(tài)的抽象,
              是對(duì)應(yīng)用執(zhí)行過(guò)程中的步驟進(jìn)行抽象,,從而獲得步驟之間的邏輯劃分。

              aop框架具有的兩個(gè)特征:
              1.各個(gè)步驟之間的良好隔離性
              2.源代碼無(wú)關(guān)性

              什么是DI機(jī)制?
              依賴(lài)注入(Dependecy Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念,具體的講:當(dāng)某個(gè)角色
              需要另外一個(gè)角色協(xié)助的時(shí)候,在傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例。但在spring中
              創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來(lái)完成,因此稱(chēng)為控制反轉(zhuǎn)。創(chuàng)建被調(diào)用者的工作由spring來(lái)完成,然后注入調(diào)用者
              因此也稱(chēng)為依賴(lài)注入。
              spring以動(dòng)態(tài)靈活的方式來(lái)管理對(duì)象 , 注入的兩種方式,設(shè)置注入和構(gòu)造注入。
              設(shè)置注入的優(yōu)點(diǎn):直觀,自然
              構(gòu)造注入的優(yōu)點(diǎn):可以在構(gòu)造器中決定依賴(lài)關(guān)系的順序。

              spring 的優(yōu)點(diǎn)都有哪些?
              1.降低了組件之間的耦合性 ,實(shí)現(xiàn)了軟件各層之間的解耦
              2.可以使用容易提供的眾多服務(wù),如事務(wù)管理,消息服務(wù)等
              3.容器提供單例模式支持
              4.容器提供了AOP技術(shù),利用它很容易實(shí)現(xiàn)如權(quán)限攔截,運(yùn)行期監(jiān)控等功能
              5.容器提供了眾多的輔助類(lèi),能加快應(yīng)用的開(kāi)發(fā)
              6.spring對(duì)于主流的應(yīng)用框架提供了集成支持,如hibernate,JPA,Struts等
              7.spring屬于低侵入式設(shè)計(jì),代碼的污染極低
              8.獨(dú)立于各種應(yīng)用服務(wù)器
              9.spring的DI機(jī)制降低了業(yè)務(wù)對(duì)象替換的復(fù)雜性
              10.Spring的高度開(kāi)放性,并不強(qiáng)制應(yīng)用完全依賴(lài)于Spring,開(kāi)發(fā)者可以自由選擇spring的部分或全部


              一、spring工作原理:

              1.spring mvc請(qǐng)所有的請(qǐng)求都提交給DispatcherServlet,它會(huì)委托應(yīng)用系統(tǒng)的其他模塊負(fù)責(zé)負(fù)責(zé)對(duì)請(qǐng)求進(jìn)行真正的處理工作。
              2.DispatcherServlet查詢(xún)一個(gè)或多個(gè)HandlerMapping,找到處理請(qǐng)求的Controller.
              3.DispatcherServlet請(qǐng)請(qǐng)求提交到目標(biāo)Controller
              4.Controller進(jìn)行業(yè)務(wù)邏輯處理后,會(huì)返回一個(gè)ModelAndView
              5.Dispathcher查詢(xún)一個(gè)或多個(gè)ViewResolver視圖解析器,找到ModelAndView對(duì)象指定的視圖對(duì)象
              6.視圖對(duì)象負(fù)責(zé)渲染返回給客戶(hù)端。


              描述一下Spring中實(shí)現(xiàn)DI(Dependency Injection)的幾種方式

              方式一:接口注入,在實(shí)際中得到了普遍應(yīng)用,即使在IOC的概念尚未確立時(shí),這樣的方法也已經(jīng)頻繁出現(xiàn)在我們的代碼中。
              方式二:Type2 IoC: Setter injection對(duì)象創(chuàng)建之后,將被依賴(lài)對(duì)象通過(guò)set方法設(shè)置進(jìn)去
              方式三:Type3 IoC: Constructor injection對(duì)象創(chuàng)建時(shí),被依賴(lài)對(duì)象以構(gòu)造方法參數(shù)的方式注入

               簡(jiǎn)單描述下IOC(inversion of control)的理解
              一個(gè)類(lèi)需要用到某個(gè)接口的方法,我們需要將類(lèi)A和接口B的實(shí)現(xiàn)關(guān)聯(lián)起來(lái),最簡(jiǎn)單的方法是類(lèi)A中創(chuàng)建一個(gè)對(duì)于接口B的實(shí)現(xiàn)C的實(shí)例,但這種方法顯然兩者的依賴(lài)(Dependency)太大了。而IoC的方法是只在類(lèi)A中定義好用于關(guān)聯(lián)接口B的實(shí)現(xiàn)的方法,將類(lèi)A,接口B和接口B的實(shí)現(xiàn)C放入IoC的 容器(Container)中,通過(guò)一定的配置由容器(Container)來(lái)實(shí)現(xiàn)類(lèi)A與接口B的實(shí)現(xiàn)C的關(guān)聯(lián)。

              spring提供的事務(wù)管理可以分為兩類(lèi):編程式的和聲明式的。編程式的,比較靈活,但是代碼量大,存在重復(fù)的代碼比較多;聲明式的比編程式的更靈活。
              編程式主要使用transactionTemplate。省略了部分的提交,回滾,一系列的事務(wù)對(duì)象定義,需注入事務(wù)管理對(duì)象.
              void add()
              {
              transactionTemplate.execute( new TransactionCallback(){
              pulic Object doInTransaction(TransactionStatus ts)
              { //do sth}
              }
              }
              聲明式:
              使用TransactionProxyFactoryBean:

              PROPAGATION_REQUIRED PROPAGATION_REQUIRED PROPAGATION_REQUIRED,readOnly
              圍繞Poxy的動(dòng)態(tài)代理 能夠自動(dòng)的提交和回滾事務(wù)
              org.springframework.transaction.interceptor.TransactionProxyFactoryBean
              PROPAGATION_REQUIRED–支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù)。這是最常見(jiàn)的選擇。
              PROPAGATION_SUPPORTS–支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行。
              PROPAGATION_MANDATORY–支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常。
              PROPAGATION_REQUIRES_NEW–新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
              PROPAGATION_NOT_SUPPORTED–以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
              PROPAGATION_NEVER–以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
              PROPAGATION_NESTED–如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類(lèi)似的操作。

              spring中的核心類(lèi)有那些,各有什么作用?
              BeanFactory:產(chǎn)生一個(gè)新的實(shí)例,可以實(shí)現(xiàn)單例模式
              BeanWrapper:提供統(tǒng)一的get及set方法
              ApplicationContext:提供框架的實(shí)現(xiàn),包括BeanFactory的所有功能
              spring的ioc及di代表什么意思?
              控制權(quán)由代碼轉(zhuǎn)向容器,通過(guò)容器動(dòng)態(tài)將某些對(duì)象加入。
              如何在spring中實(shí)現(xiàn)國(guó)際化?
              在applicationContext.xml加載一個(gè)bean
              message.properties是一個(gè)鍵名加鍵值的文件,

















































               

              posted @ 2013-05-29 14:37 abin 閱讀(642) | 評(píng)論 (0)編輯 收藏

              Java堆,分配對(duì)象實(shí)例所在空間,是GC的主要對(duì)象。分為
               新生代(Young Generation/New)
               老年代(Tenured Generation/Old)
              新生代又劃分成
               Eden Space
               From Survivor/Survivor 0
               To Survivor/Survivor 1
              新生代要如此劃分是因?yàn)樾律褂玫腉C算法是復(fù)制收集算法。這種算法效率較高,而GC主要是發(fā)生在對(duì)象經(jīng)常消亡的新生代,因此新生代適合使用這種復(fù)制收集算法。由于有一個(gè)假設(shè):在一次新生代的GC(Minor GC)后大部分的對(duì)象占用的內(nèi)存都會(huì)被回收,因此留存的放置GC后仍然活的對(duì)象的空間就比較小了。這個(gè)留存的空間就是Survivor space:From Survivor或To Survivor。這兩個(gè)Survivor空間是一樣大小的。例如,新生代大小是10M(Xmn10M),那么缺省情況下(-XX:SurvivorRatio=8),Eden Space 是8M,F(xiàn)rom和To都是1M。
              在new一個(gè)對(duì)象時(shí),先在Eden Space上分配,如果Eden Space空間不夠就要做一次Minor GC。Minor GC后,要把Eden和From中仍然活著的對(duì)象們復(fù)制到To空間中去。如果To空間不能容納Minor GC后活著的某個(gè)對(duì)象,那么該對(duì)象就被promote到老年代空間。從Eden空間被復(fù)制到To空間的對(duì)象就有了age=1。此age=1的對(duì)象如果在下一次的Minor GC后仍然存活,它還會(huì)被復(fù)制到另一個(gè)Survivor空間(如果認(rèn)為From和To是固定的,就是又從To回到了From空間),而它的age=2。如此反復(fù),如果age大于某個(gè)閾值(-XX:MaxTenuringThreshold=n),那個(gè)該對(duì)象就也可以promote到老年代了。
              如果Survivor空間中相同age(例如,age=5)對(duì)象的總和大于等于Survivor空間的一半,那么age>=5的對(duì)象在下一次Minor GC后就可以直接promote到老年代,而不用等到age增長(zhǎng)到閾值。
              在做Minor GC時(shí),只對(duì)新生代做回收,不會(huì)回收老年代。即使老年代的對(duì)象無(wú)人索引也將仍然存活,直到下一次Full GC。
              posted @ 2013-05-28 14:31 abin 閱讀(455) | 評(píng)論 (0)編輯 收藏

              切分(Sharding)并不是特定數(shù)據(jù)庫(kù)產(chǎn)品所附屬的功能,而是在具體技術(shù)細(xì)節(jié)之上的抽象處理。是水平擴(kuò)展(Scale Out)的解決方案,主要目的是解決單節(jié)點(diǎn)數(shù)據(jù)庫(kù)服務(wù)器的能力限制,以及整個(gè)應(yīng)用其架構(gòu)的可擴(kuò)展性(Scalability)。

              切分主要有兩種方式:水平切分(Horizental Sharding)和垂直切分(Vertical Sharding)。

              水平切分所指的是通過(guò)一系列的切分規(guī)則將數(shù)據(jù)水平分布到不同的DB或table中,在通過(guò)相應(yīng)的DB路由 或者table路由規(guī)則找到需要查詢(xún)的具體的DB或者table以進(jìn)行Query操作,比如根據(jù)用戶(hù)ID將用戶(hù)表切分到多臺(tái)數(shù)據(jù)庫(kù)上。

              垂直切分指的是按業(yè)務(wù)、產(chǎn)品切分,將不同類(lèi)型的數(shù)據(jù)且分到不同的服務(wù)器上,通過(guò)數(shù)據(jù)庫(kù)代理疏通程序與多個(gè)數(shù)據(jù)庫(kù)的通訊、降低應(yīng)用的復(fù)雜度。

              讀寫(xiě)分離簡(jiǎn)單的說(shuō)是把對(duì)數(shù)據(jù)庫(kù)讀和寫(xiě)的操作分開(kāi)對(duì)應(yīng)不同的數(shù)據(jù)庫(kù)服務(wù)器,這樣能有效地減輕數(shù)據(jù)庫(kù)壓力,也能減輕io壓力。主數(shù)據(jù)庫(kù)提供寫(xiě)操作,從數(shù)據(jù)庫(kù)提供讀操作,這樣既避免了主數(shù)據(jù)庫(kù)服務(wù)器(Master)的過(guò)載,也有效地利用了從數(shù)據(jù)庫(kù)服務(wù)器(Slave)的資源。

              這里ebay工程師的文章:《可伸縮性最佳實(shí)踐:來(lái)自eBay的經(jīng)驗(yàn)》更詳細(xì)地介紹了一些概念及業(yè)務(wù)場(chǎng)景。

              --End--

              posted @ 2013-05-27 10:14 abin 閱讀(858) | 評(píng)論 (0)編輯 收藏

              Java內(nèi)存分配:

              1. 寄存器:我們?cè)诔绦蛑袩o(wú)法控制
              2. 棧:存放基本類(lèi)型的數(shù)據(jù)和對(duì)象的引用,但對(duì)象本身不存放在棧中,而是存放在堆中
              3. 堆:存放用new產(chǎn)生的數(shù)據(jù)
              4. 靜態(tài)域:存放在對(duì)象中用static定義的靜態(tài)成員
              5. 常量池:存放常量
              6. 非RAM(隨機(jī)存取存儲(chǔ)器)存儲(chǔ):硬盤(pán)等永久存儲(chǔ)空間
              ----------------------------------------------------------------------------------------------------------------------

              a.在函數(shù)中定義的一些基本類(lèi)型的變量數(shù)據(jù)和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配。  
              當(dāng)在一段代碼塊定義一個(gè)變量時(shí),Java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)該變量退出該作用域后,Java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間,該內(nèi)存空間可以立即被另作他用。  
              b.堆內(nèi)存用來(lái)存放由new創(chuàng)建的對(duì)象和數(shù)組。 在堆中分配的內(nèi)存,由Java虛擬機(jī)的自動(dòng)垃圾回收器來(lái)管理。  
              在堆中產(chǎn)生了一個(gè)數(shù)組或?qū)ο蠛螅€可以在棧中定義一個(gè)特殊的變量,讓棧中這個(gè)變量的取值等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址,棧中的這個(gè)變量就成了數(shù)組或?qū)ο蟮囊米兞俊?nbsp; 引用變量就相當(dāng)于是為數(shù)組或?qū)ο笃鸬囊粋€(gè)名稱(chēng),以后就可以在程序中使用棧中的引用變量來(lái)訪問(wèn)堆中的數(shù)組或?qū)ο蟆R米兞烤拖喈?dāng)于是為 數(shù)組或者對(duì)象起的一個(gè)名稱(chēng)。引用變量是普通的變量,定義時(shí)在棧中分配,引用變量在程序運(yùn)行到其作用域之外后被釋放。而數(shù)組和對(duì)象本身在堆中分配,即使程序運(yùn)行到使用 new 產(chǎn)生數(shù)組或者對(duì)象的語(yǔ)句所在的代碼塊之外,數(shù)組和對(duì)象本身占據(jù)的內(nèi)存不會(huì)被釋放,數(shù)組和對(duì)象在沒(méi)有引用變量指向它的時(shí)候,才變?yōu)槔荒茉诒皇褂茫匀徽紦?jù)內(nèi)存空間不放,在隨后的一個(gè)不確定的時(shí)間被垃圾回收器收走(釋放掉)。這也是 Java 比較占內(nèi)存的原因。 
              實(shí)際上,棧中的變量指向堆內(nèi)存中的變量,這就是 Java 中的指針! 
              c.常量池(constant pool)指的是在編譯期被確定,并被保存在已編譯的.class文件中的一些數(shù)據(jù)。除了包含代碼中所定義的各種基本類(lèi)型(如int、long等等)和對(duì)象型(如String及數(shù)組)的常量值(final)還包含一些以文本形式出現(xiàn)的符號(hào)引用,比如: 類(lèi)和接口的全限定名; 字段的名稱(chēng)和描述符; 方法和名稱(chēng)和描述符。 虛擬機(jī)必須為每個(gè)被裝載的類(lèi)型維護(hù)一個(gè)常量池。常量池就是該類(lèi)型所用到常量的一個(gè)有序集和,包括直接常量(string,integer和floating point常量)和對(duì)其他類(lèi)型,字段和方法的符號(hào)引用。對(duì)于String常量,它的值是在常量池中的。而JVM中的常量池在內(nèi)存當(dāng)中是以表的形式存在的,對(duì)于String類(lèi)型,有一張固定長(zhǎng)度的CONSTANT_String_info表用來(lái)存儲(chǔ)文字字符串值,注意:該表只存儲(chǔ)文字字符串值,不存儲(chǔ)符號(hào)引用。說(shuō)到這里,對(duì)常量池中的字符串值的存儲(chǔ)位置應(yīng)該有一個(gè)比較明了的理解了。

              在程序執(zhí)行的時(shí)候,常量池會(huì)儲(chǔ)存在Method Area,而不是堆中.
              d.Java的堆是一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū),類(lèi)的(對(duì)象從中分配空間。這些對(duì)象通過(guò)new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來(lái)顯式的釋放。堆是由垃圾回收來(lái)負(fù)責(zé)的,堆的優(yōu)勢(shì)是可以動(dòng)態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。但缺點(diǎn)是,由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。 

              棧的優(yōu)勢(shì)是,存取速度比堆要快,僅次于寄存器,棧數(shù)據(jù)可以共享。但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類(lèi)型的變量數(shù)據(jù)(int, short, long, byte, float, double, boolean, char)和對(duì)象句柄(引用)。 

              棧有一個(gè)很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。假設(shè)我們同時(shí)定義: 
              int a = 3; 
              int b = 3; 
              編譯器先處理int a = 3;首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a的引用,然后查找棧中是否有3這個(gè)值,如果沒(méi)找到,就將3存放進(jìn)來(lái),然后將a指向3。接著處理int b = 3;在創(chuàng)建完b的引用變量后,因?yàn)樵跅V幸呀?jīng)有3這個(gè)值,便將b直接指向3。這樣,就出現(xiàn)了a與b同時(shí)均指向3的情況。 

              這時(shí),如果再令a=4;那么編譯器會(huì)重新搜索棧中是否有4值,如果沒(méi)有,則將4存放進(jìn)來(lái),并令a指向4;如果已經(jīng)有了,則直接將a指向這個(gè)地址。因此a值的改變不會(huì)影響到b的值。 

              要注意這種數(shù)據(jù)的共享與兩個(gè)對(duì)象的引用同時(shí)指向一個(gè)對(duì)象的這種共享是不同的,因?yàn)檫@種情況a的修改并不會(huì)影響到b, 它是由編譯器完成的,它有利于節(jié)省空間。而一個(gè)對(duì)象引用變量修改了這個(gè)對(duì)象的內(nèi)部狀態(tài),會(huì)影響到另一個(gè)對(duì)象引用變量。

              /*****************************************************************************/

               

              String是一個(gè)特殊的包裝類(lèi)數(shù)據(jù)。可以用: 
              String str = new String("abc"); 
              String str = "abc"; 
              兩種的形式來(lái)創(chuàng)建,第一種是用new()來(lái)新建對(duì)象的,它會(huì)在存放于堆中。每調(diào)用一次就會(huì)創(chuàng)建一個(gè)新的對(duì)象。 
              而第二種是先在棧中創(chuàng)建一個(gè)對(duì)String類(lèi)的對(duì)象引用變量str,然后通過(guò)符號(hào)引用去字符串常量池里找有沒(méi)有"abc",如果沒(méi)有,則將"abc"存放進(jìn)字符串常量池,并令str指向”abc”,如果已經(jīng)有”abc” 則直接令str指向“abc”。 

              比較類(lèi)里面的數(shù)值是否相等時(shí),用equals()方法;當(dāng)測(cè)試兩個(gè)包裝類(lèi)的引用是否指向同一個(gè)對(duì)象時(shí),用==,下面用例子說(shuō)明上面的理論。 
              String str1 = "abc"; 
              String str2 = "abc"; 
              System.out.println(str1==str2); //true 
              可以看出str1和str2是指向同一個(gè)對(duì)象的。 

              String str1 =new String ("abc"); 
              String str2 =new String ("abc"); 
              System.out.println(str1==str2); // false 
              用new的方式是生成不同的對(duì)象。每一次生成一個(gè)。 

              因此用第二種方式創(chuàng)建多個(gè)”abc”字符串,在內(nèi)存中其實(shí)只存在一個(gè)對(duì)象而已. 這種寫(xiě)法有利與節(jié)省內(nèi)存空間. 同時(shí)它可以在一定程度上提高程序的運(yùn)行速度,因?yàn)镴VM會(huì)自動(dòng)根據(jù)棧中數(shù)據(jù)的實(shí)際情況來(lái)決定是否有必要?jiǎng)?chuàng)建新對(duì)象。而對(duì)于String str = new String("abc");的代碼,則一概在堆中創(chuàng)建新對(duì)象,而不管其字符串值是否相等,是否有必要?jiǎng)?chuàng)建新對(duì)象,從而加重了程序的負(fù)擔(dān)。 

              另一方面, 要注意: 我們?cè)谑褂弥T如String str = "abc";的格式定義類(lèi)時(shí),總是想當(dāng)然地認(rèn)為,創(chuàng)建了String類(lèi)的對(duì)象str。擔(dān)心陷阱!對(duì)象可能并沒(méi)有被創(chuàng)建!而可能只是指向一個(gè)先前已經(jīng)創(chuàng)建的對(duì)象。只有通過(guò)new()方法才能保證每次都創(chuàng)建一個(gè)新的對(duì)象。 
              由于String類(lèi)的immutable性質(zhì),當(dāng)String變量需要經(jīng)常變換其值時(shí),應(yīng)該考慮使用StringBuffer類(lèi),以提高程序效率。 
              1. 首先String不屬于8種基本數(shù)據(jù)類(lèi)型,String是一個(gè)對(duì)象。 
              因?yàn)閷?duì)象的默認(rèn)值是null,所以String的默認(rèn)值也是null;但它又是一種特殊的對(duì)象,有其它對(duì)象沒(méi)有的一些特性。 

              2. new String()和new String(”")都是申明一個(gè)新的空字符串,是空串不是null; 

              3. String str=”kvill”;String str=new String (”kvill”)的區(qū)別

              看例1: 

              String s0="kvill"; 
              String s1="kvill"; 
              String s2="kv" + "ill"; 
              System.out.println( s0==s1 ); 
              System.out.println( s0==s2 ); 
              結(jié)果為: 
              true 
              true 

              首先,我們要知結(jié)果為道Java會(huì)確保一個(gè)字符串常量只有一個(gè)拷貝。 
              因?yàn)槔又械膕0和s1中的”kvill”都是字符串常量,它們?cè)诰幾g期就被確定了,所以s0==s1為true;而”kv”和”ill”也都是字符串常量,當(dāng)一個(gè)字符串由多個(gè)字符串常量連接而成時(shí),它自己肯定也是字符串常量,所以s2也同樣在編譯期就被解析為一個(gè)字符串常量,所以s2也是常量池中” kvill”的一個(gè)引用。所以我們得出s0==s1==s2;用new String() 創(chuàng)建的字符串不是常量,不能在編譯期就確定,所以new String() 創(chuàng)建的字符串不放入常量池中,它們有自己的地址空間。 

              看例2: 
              String s0="kvill"; 
              String s1=new String("kvill"); 
              String s2="kv" + new String("ill"); 
              System.out.println( s0==s1 ); 
              System.out.println( s0==s2 ); 
              System.out.println( s1==s2 ); 
              結(jié)果為: 
              false 
              false 
              false 

              例2中s0還是常量池中"kvill”的應(yīng)用,s1因?yàn)闊o(wú)法在編譯期確定,所以是運(yùn)行時(shí)創(chuàng)建的新對(duì)象”kvill”的引用,s2因?yàn)橛泻蟀氩糠?new String(”ill”)所以也無(wú)法在編譯期確定,所以也是一個(gè)新創(chuàng)建對(duì)象”kvill”的應(yīng)用;明白了這些也就知道為何得出此結(jié)果了。 

              4. String.intern(): 
              再補(bǔ)充介紹一點(diǎn):存在于.class文件中的常量池,在運(yùn)行期被JVM裝載,并且可以擴(kuò)充。String的intern()方法就是擴(kuò)充常量池的 一個(gè)方法;當(dāng)一個(gè)String實(shí)例str調(diào)用intern()方法時(shí),Java查找常量池中是否有相同Unicode的字符串常量,如果有,則返回其的引用,如果沒(méi)有,則在常量池中增加一個(gè)Unicode等于str的字符串并返回它的引用;看例3就清楚了 

              例3: 
              String s0= "kvill"; 
              String s1=new String("kvill"); 
              String s2=new String("kvill"); 
              System.out.println( s0==s1 ); 
              System.out.println( "**********" ); 
              s1.intern(); 
              s2=s2.intern(); //把常量池中"kvill"的引用賦給s2 
              System.out.println( s0==s1); 
              System.out.println( s0==s1.intern() ); 
              System.out.println( s0==s2 ); 
              結(jié)果為: 
              false 
              ********** 
              false //雖然執(zhí)行了s1.intern(),但它的返回值沒(méi)有賦給s1 
              true //說(shuō)明s1.intern()返回的是常量池中"kvill"的引用 
              true 

              最后我再破除一個(gè)錯(cuò)誤的理解:有人說(shuō),“使用 String.intern() 方法則可以將一個(gè) String 類(lèi)的保存到一個(gè)全局 String 表中 ,如果具有相同值的 Unicode 字符串已經(jīng)在這個(gè)表中,那么該方法返回表中已有字符串的地址,如果在表中沒(méi)有相同值的字符串,則將自己的地址注冊(cè)到表中”如果我把他說(shuō)的這個(gè)全局的 String 表理解為常量池的話,他的最后一句話,”如果在表中沒(méi)有相同值的字符串,則將自己的地址注冊(cè)到表中”是錯(cuò)的: 

              看例4: 
              String s1=new String("kvill"); 
              String s2=s1.intern(); 
              System.out.println( s1==s1.intern() ); 
              System.out.println( s1+" "+s2 ); 
              System.out.println( s2==s1.intern() ); 
              結(jié)果: 
              false 
              kvill kvill 
              true 

              在這個(gè)類(lèi)中我們沒(méi)有聲名一個(gè)”kvill”常量,所以常量池中一開(kāi)始是沒(méi)有”kvill”的,當(dāng)我們調(diào)用s1.intern()后就在常量池中新添加了一個(gè)”kvill”常量,原來(lái)的不在常量池中的”kvill”仍然存在,也就不是“將自己的地址注冊(cè)到常量池中”了。 
              s1==s1.intern()為false說(shuō)明原來(lái)的”kvill”仍然存在;s2現(xiàn)在為常量池中”kvill”的地址,所以有s2==s1.intern()為true。 

              5. 關(guān)于equals()和==: 
              這個(gè)對(duì)于String簡(jiǎn)單來(lái)說(shuō)就是比較兩字符串的Unicode序列是否相當(dāng),如果相等返回true;而==是比較兩字符串的地址是否相同,也就是是否是同一個(gè)字符串的引用。 

              6. 關(guān)于String是不可變的 
              這一說(shuō)又要說(shuō)很多,大家只要知道String的實(shí)例一旦生成就不會(huì)再改變了,比如說(shuō):String str=”kv”+”ill”+” “+”ans”; 就是有4個(gè)字符串常量,首先”kv”和”ill”生成了”kvill”存在內(nèi)存中,然后”kvill”又和” ” 生成 “kvill “存在內(nèi)存中,最后又和生成了”kvill ans”;并把這個(gè)字符串的地址賦給了str,就是因?yàn)镾tring的”不可變”產(chǎn)生了很多臨時(shí)變量,這也就是為什么建議用StringBuffer的原因了,因?yàn)镾tringBuffer是可改變的。

              /*******************************************************************************/

              下面是一些String相關(guān)的常見(jiàn)問(wèn)題:

              String中的final用法和理解
              final StringBuffer a = new StringBuffer("111");
              final StringBuffer b = new StringBuffer("222");
              a=b;//此句編譯不通過(guò)

              final StringBuffer a = new StringBuffer("111");
              a.append("222");//編譯通過(guò)

              可見(jiàn),final只對(duì)引用的"值"(即內(nèi)存地址)有效,它迫使引用只能指向初始指向的那個(gè)對(duì)象,改變它的指向會(huì)導(dǎo)致編譯期錯(cuò)誤。至于它所指向的對(duì)象的變化,final是不負(fù)責(zé)的。

              String 常量池問(wèn)題的幾個(gè)例子

              下面是幾個(gè)常見(jiàn)例子的比較分析和理解:
              [1]
              String a = "a1"; 
              String b = "a" + 1; 
              System.out.println((a == b)); //result = true
              String a = "atrue"; 
              String b = "a" + "true"; 
              System.out.println((a == b)); //result = true
              String a = "a3.4"; 
              String b = "a" + 3.4; 
              System.out.println((a == b)); //result = true

              分析:JVM對(duì)于字符串常量的"+"號(hào)連接,將程序編譯期,JVM就將常量字符串的"+"連接優(yōu)化為連接后的值,拿"a" + 1來(lái)說(shuō),經(jīng)編譯器優(yōu)化后在class中就已經(jīng)是a1。在編譯期其字符串常量的值就確定下來(lái),故上面程序最終的結(jié)果都為true。

              [2]
              String a = "ab"; 
              String bb = "b"; 
              String b = "a" + bb; 
              System.out.println((a == b)); //result = false

              分析:JVM對(duì)于字符串引用,由于在字符串的"+"連接中,有字符串引用存在,而引用的值在程序編譯期是無(wú)法確定的,即"a" + bb無(wú)法被編譯器優(yōu)化,只有在程序運(yùn)行期來(lái)動(dòng)態(tài)分配并將連接后的新地址賦給b。所以上面程序的結(jié)果也就為false。

              [3]
              String a = "ab"; 
              final String bb = "b"; 
              String b = "a" + bb; 
              System.out.println((a == b)); //result = true

              分析:和[3]中唯一不同的是bb字符串加了final修飾,對(duì)于final修飾的變量,它在編譯時(shí)被解析為常量值的一個(gè)本地拷貝存儲(chǔ)到自己的常量池中或嵌入到它的字節(jié)碼流中。所以此時(shí)的"a" + bb和"a" + "b"效果是一樣的。故上面程序的結(jié)果為true。

              [4]
              String a = "ab"; 
              final String bb = getBB(); 
              String b = "a" + bb; 
              System.out.println((a == b)); //result = false 
              private static String getBB() {
              return "b"; 
              }

              分析:JVM對(duì)于字符串引用bb,它的值在編譯期無(wú)法確定,只有在程序運(yùn)行期調(diào)用方法后,將方法的返回值和"a"來(lái)動(dòng)態(tài)連接并分配地址為b,故上面程序的結(jié)果為false。

              通過(guò)上面4個(gè)例子可以得出得知:
              String  s  =  "a" + "b" + "c";   
              就等價(jià)于String s = "abc";   

              String  a  =  "a";   
              String  b  =  "b";   
              String  c  =  "c";   
              String  s  =   a  +  b  +  c;  

              這個(gè)就不一樣了,最終結(jié)果等于:   
              StringBuffer temp = new StringBuffer();   
              temp.append(a).append(b).append(c);   
              String s = temp.toString();

              由上面的分析結(jié)果,可就不難推斷出String 采用連接運(yùn)算符(+)效率低下原因分析,形如這樣的代碼:

              public class Test {
              public static void main(String args[]) {
              String s = null;
              for(int i = 0; i < 100; i++) {
              s += "a";
              }
              }
              }

              每做一次 + 就產(chǎn)生個(gè)StringBuilder對(duì)象,然后append后就扔掉。下次循環(huán)再到達(dá)時(shí)重新產(chǎn)生個(gè)StringBuilder對(duì)象,然后 append 字符串,如此循環(huán)直至結(jié)束。 如果我們直接采用 StringBuilder 對(duì)象進(jìn)行 append 的話,我們可以節(jié)省 N - 1 次創(chuàng)建和銷(xiāo)毀對(duì)象的時(shí)間。所以對(duì)于在循環(huán)中要進(jìn)行字符串連接的應(yīng)用,一般都是用StringBuffer或StringBulider對(duì)象來(lái)進(jìn)行append操作。

              String對(duì)象的intern方法理解和分析:

              public class Test4 {
              private static String a = "ab"; 
              public static void main(String[] args){
              String s1 = "a";
              String s2 = "b";
              String s = s1 + s2;
              System.out.println(s == a);//false
              System.out.println(s.intern() == a);//true  
              }
              }

              這里用到Java里面是一個(gè)常量池的問(wèn)題。對(duì)于s1+s2操作,其實(shí)是在堆里面重新創(chuàng)建了一個(gè)新的對(duì)象,s保存的是這個(gè)新對(duì)象在堆空間的的內(nèi)容,所以s與a的值是不相等的。而當(dāng)調(diào)用s.intern()方法,卻可以返回s在常量池中的地址值,因?yàn)閍的值存儲(chǔ)在常量池中,故s.intern和a的值相等

               

              總結(jié):

              a.棧中用來(lái)存放一些原始數(shù)據(jù)類(lèi)型的局部變量數(shù)據(jù)和對(duì)象的引用(String,數(shù)組.對(duì)象等等)但不存放對(duì)象內(nèi)容

              b.堆中存放使用new關(guān)鍵字創(chuàng)建的對(duì)象.

              c.字符串是一個(gè)特殊包裝類(lèi),其引用是存放在棧里的,而對(duì)象內(nèi)容必須根據(jù)創(chuàng)建方式不同定(常量池和堆).有的是編譯期就已經(jīng)創(chuàng)建好,存放在字符串常量池中,而有的是運(yùn)行時(shí)才被創(chuàng)建.使用new關(guān)鍵字,存放在堆中。

               

              本文轉(zhuǎn)自:http://zy19880423.javaeye.com/blog/434179

              posted @ 2013-05-23 22:39 abin 閱讀(518) | 評(píng)論 (0)編輯 收藏

              1. Snapshot版本代表不穩(wěn)定、尚處于開(kāi)發(fā)中的版本 

              2. Release版本則代表穩(wěn)定的版本 

              3. 什么情況下該用SNAPSHOT? 
                   協(xié)同開(kāi)發(fā)時(shí),如果A依賴(lài)構(gòu)件B,由于B會(huì)更新,B應(yīng)該使用SNAPSHOT來(lái)標(biāo)識(shí)自己。這種做法的必要性可以反證如下: 

                    a.如果B不用SNAPSHOT,而是每次更新后都使用一個(gè)穩(wěn)定的版本,那版本號(hào)就會(huì)升得太快,每天一升甚至每個(gè)小時(shí)一升,這就是對(duì)版本號(hào)的濫用。 

                    b.如果B不用SNAPSHOT, 但一直使用一個(gè)單一的Release版本號(hào),那當(dāng)B更新后,A可能并不會(huì)接受到更新。因?yàn)锳所使用的repository一般不會(huì)頻繁更新release版本的緩存(即本地repository),所以B以不換版本號(hào)的方式更新后,A在拿B時(shí)發(fā)現(xiàn)本地已有這個(gè)版本,就不會(huì)去遠(yuǎn)程Repository下載最新的B 

              4. 不用Release版本,在所有地方都用SNAPSHOT版本行不行?      
                   不行。正式環(huán)境中不得使用snapshot版本的庫(kù)。 比如說(shuō),今天你依賴(lài)某個(gè)snapshot版本的第三方庫(kù)成功構(gòu)建了自己的應(yīng)用,明天再構(gòu)建時(shí)可能就會(huì)失敗,因?yàn)榻裢淼谌娇赡芤呀?jīng)更新了它的snapshot庫(kù)。你再次構(gòu)建時(shí),Maven會(huì)去遠(yuǎn)程repository下載snapshot的最新版本,你構(gòu)建時(shí)用的庫(kù)就是新的jar文件了,這時(shí)正確性就很難保證了。
              posted @ 2013-05-23 15:21 abin 閱讀(528) | 評(píng)論 (0)編輯 收藏

              僅列出標(biāo)題
              共50頁(yè): First 上一頁(yè) 11 12 13 14 15 16 17 18 19 下一頁(yè) Last 
              主站蜘蛛池模板: 甘泉县| 徐汇区| 郯城县| 五台县| 台安县| 周至县| 宁都县| 武清区| 滨州市| 凤山县| 皋兰县| 湟中县| 上杭县| 万年县| 东明县| 施秉县| 天峨县| 什邡市| 揭东县| 兰考县| 北票市| 万全县| 玛曲县| 惠来县| 交城县| 元谋县| 汝州市| 正镶白旗| 芜湖市| 迭部县| 乐安县| 和静县| 蚌埠市| 南岸区| 河间市| 宁安市| 泰宁县| 安陆市| 清远市| 潮安县| 安顺市|