super

          2007年3月14日 #

          使用 apache common dbcp +common pool+mysql連接無效的問題




          Throwable occurred: org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 50,123,505 milliseconds ago.  The last packet sent successfully to the server was 50,123,505 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.


          這主要是由兩個(gè)原因引起來的:
          1.mysql 會(huì)自動(dòng)關(guān)閉長(zhǎng)時(shí)間不用的connection,一個(gè)連接如果處于sleep狀態(tài)達(dá)到mysql的參數(shù)wait_timeout指定的時(shí)間(默認(rèn)為8小時(shí)),就是自動(dòng)關(guān)閉這個(gè)連接
          2.common pool中沒有指定相應(yīng)的連接檢查參數(shù)


          解決辦法:從common pool的配置參數(shù)來解決:

           <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName">
             <value>${db.driver}</value>
            </property>
            <property name="url">
             <value>${db.url}</value>
            </property>
            <property name="username">
             <value>${db.user}</value>
            </property>
            <property name="password">
             <value>${db.password}</value>
            </property>
            <property name="maxActive">
             <value>100</value>
            </property>
            <property name="maxIdle">
             <value>50</value>
            </property>
            <property name="maxWait">
             <value>10000</value>
            </property>


            <property name="timeBetweenEvictionRunsMillis">
             <value>3600000</value><!--1 hours-->
            </property>

          <!--
            <property name="minEvictableIdleTimeMillis">
             <value>20000</value>
            </property>
          -->
            
            <property name="testWhileIdle">
             <value>true</value>
            </property>
            <property name="validationQuery">
             <value>select 1 from dual</value>
            </property>

           </bean>

          使用上述的三個(gè)紅色的參數(shù),就可以避免這個(gè)問題.這三個(gè)參數(shù)的意義:

          timeBetweenEvictionRunsMillis:啟動(dòng)connection校驗(yàn)定時(shí)器,定時(shí)器運(yùn)行時(shí)間間隔就是timeBetweenEvictionRunsMillis的值.默認(rèn)為-1,表示不啟動(dòng)定時(shí)器,這里設(shè)定為1小時(shí),只要小于mysql的wait_timeout就可以了

          testWhileIdle: true,表示檢查idle的connection,false為不檢查

          validationQuery:用于檢查connection的sql語(yǔ)句.


          這只是一種方法,另外的幾種方法:

          timeBetweenEvictionRunsMillis+minEvictableIdleTimeMillis:這種方式不檢查Connection的有效性,而是檢查連接的空閑時(shí)間,大于minEvictableIdleTimeMillis就清除.

            <property name="timeBetweenEvictionRunsMillis">
             <value>3600000</value><!--1 hours-->
            </property>

            <property name="minEvictableIdleTimeMillis">
             <value>120000</value><!--connection的空閑時(shí)間大于這個(gè)值,就直接被關(guān)閉,并從連接池中刪除-->
            </property>


          如果不喜歡用定時(shí)器,也可以配置testOnBorrow+validationQuery參數(shù):每次從連接池取參數(shù)都會(huì)校驗(yàn)連接的有效性.實(shí)際上這種方式性能會(huì)比定時(shí)器差些.
            <property name="testOnBorrow">
             <value>true</value>
            </property>
            <property name="validationQuery">
             <value>select 1 from dual</value>
            </property>


          另外,也可以用testOnReturn+validationQuery,不過未必會(huì)解決問題:這表示每次使用完連接,歸還連接池的時(shí)候檢查連接的有效性,這有可能導(dǎo)致使用一次無效的連接,最好不要用.


          上面的幾種方法可以合并使用,只是檢查的點(diǎn)多了,未必是好事


          另外,也可以使用Abandoned的那幾個(gè)參數(shù),來刪除連接池中的連接.也能達(dá)到效果.我沒測(cè)試.











          posted @ 2010-09-15 17:57 王衛(wèi)華 閱讀(2448) | 評(píng)論 (0)編輯 收藏

          android中點(diǎn)中overlay彈出帶尾巴的氣泡的實(shí)現(xiàn)




          就是上面的樣子

          做這個(gè)過程中我碰到兩個(gè)問題:
          1:如何做帶尾巴的氣泡View
          2:如何把這個(gè)View添加到MapView中.


          1:如何做帶尾巴的氣泡View
          我是采用背景圖的方式來實(shí)現(xiàn)的.當(dāng)然,普通的PNG在View 縮放的時(shí)候會(huì)失真,尤其是那個(gè)尖尖的尾巴.
          后來采用9.png的格式,才完成了不變形的效果.9.png格式的Png可以用SDK\Tools\draw9patch.bat來處理,只要把普通的png的邊上標(biāo)志一下就可以了,具體draw9patch.bat如何使用這里就不說了,網(wǎng)上有很多文檔,自己查查就知道了.
          我生成的9.png就是下面這個(gè)樣子,注意四周的黑線.就是9png拉伸時(shí)的標(biāo)識(shí)


          有了這個(gè)png,直接放到你的工程下的res/drawable目錄就可以了,
          然后在res/layout目錄下建立你的view的xml文件,比如叫overlay_pop.xml,我的是這樣的:

          <?xml version="1.0" encoding="UTF-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:orientation="vertical"
           android:background="@drawable/bubble_background" <!--這就是那個(gè)9.png-->
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:paddingLeft="5px"
           android:paddingTop="5px"
           android:paddingRight="5px"
           android:paddingBottom="20px"    <!--注意加上padding,否則view里面的東西就畫到邊框上了-->
             >
              <TextView android:id="@+id/map_bubbleTitle"
                 android:ellipsize="marquee"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:gravity="center_horizontal"
                 android:singleLine="true"
                 style="@style/map_BubblePrimary" /> <!--style可以沒有,我這里第一個(gè)TextView表示標(biāo)題,用的是大字體-->
              <TextView  android:id="@+id/map_bubbleText"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:singleLine="false"
                 style="@style/map_BubbleSecondary" /><!--style可以沒有,我這里第二個(gè)TextView表示描述信息,用的是大字體-->
          </LinearLayout>


          這樣popView就建立好了


          2:如何把這個(gè)View添加到MapView中.
          通常是在mapView中點(diǎn)擊某個(gè)位置,彈出popView
          或者點(diǎn)擊某個(gè)Overlay彈出popView,這里用點(diǎn)擊Overlay來說明,

          overlay有onTap()方法,你可以實(shí)現(xiàn)自己的overlay,overideonTap()方法,彈出popView,
          也可以使用setOnFocusChangeListener(),在listener中實(shí)現(xiàn)彈出popView,.
          我是用的listener,因?yàn)閟etOnFocusChangeListener在失去焦點(diǎn)也會(huì)觸發(fā),我可以再失去焦點(diǎn)的時(shí)候隱藏popView.

          MapView是繼承自ViewGroup的,因此,MapView有addView()方法,同時(shí)還有MapView.LayoutParams
          MapView.LayoutParams 可以根據(jù)GeoPoint來定位,我就是利用這個(gè)特性來定位彈出的popView的.

          PointItemizedOverlay overlay = new PointItemizedOverlay(drawable); <!--這是我繼承自ItemizedOverlay的overlay,主要就是畫一個(gè)圖片,寫一個(gè)名稱,很簡(jiǎn)單,這里不貼具體代碼了-->

          public class BaseMapActivity extends MapActivity {

           /**
            * 地圖View
            */
           protected MapView mapView;

           /**
            * 彈出的氣泡View
            */
           private View popView;
          /**
              監(jiān)聽器
          */
           private final ItemizedOverlay.OnFocusChangeListener onFocusChangeListener = new ItemizedOverlay.OnFocusChangeListener() {

            @Override
            public void onFocusChanged(ItemizedOverlay overlay, OverlayItem newFocus) {
                //創(chuàng)建氣泡窗口
           

             if (popView != null) {
                popView.setVisibility(View.GONE);
             }

             if (newFocus != null) {

              MapView.LayoutParams geoLP = (MapView.LayoutParams) popView.getLayoutParams();
              geoLP.point = newFocus.getPoint();//這行用于popView的定位
              TextView title = (TextView) popView.findViewById(R.id.map_bubbleTitle);
              title.setText(newFocus.getTitle());

              TextView desc = (TextView) popView.findViewById(R.id.map_bubbleText);
              if (newFocus.getSnippet() == null || newFocus.getSnippet().length() == 0) {
               desc.setVisibility(View.GONE);
              } else {
               desc.setVisibility(View.VISIBLE);
               desc.setText(newFocus.getSnippet());
              }
              mapView.updateViewLayout(popView, geoLP);
              popView.setVisibility(View.VISIBLE);
             }
            }
           };




               public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                      /**
                      省略其他代碼
                     **/

                    //初始化氣泡,并設(shè)置為不可見

                 popView = inflater.inflate(R.layout.overlay_popup, null);
                 mapView.addView( popView,
                       new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT,
                     null, MapView.LayoutParams.BOTTOM_CENTER));
                    //由于我的氣泡的尾巴是在下邊居中的,因此要設(shè)置成MapView.LayoutParams.BOTTOM_CENTER.
                    //這里沒有給GeoPoint,在onFocusChangeListener中設(shè)置
                 views.add(popView);
                popView.setVisibility(View.GONE);

              添加overlay
              PointItemizedOverlay overlay = new PointItemizedOverlay(drawable);
              //設(shè)置顯示/隱藏泡泡的監(jiān)聽器
              overlay.setOnFocusChangeListener(onFocusChangeListener);
              overlay.addOverlay(/*你自己的overlayItem*/);
              overlay.addOverlay(/*你自己的overlayItem*/);
              overlay.addOverlay(/*你自己的overlayItem*/);

              }
          }

          這樣就基本完工了.




          posted @ 2010-08-12 15:03 王衛(wèi)華 閱讀(5706) | 評(píng)論 (7)編輯 收藏

          android mapView中畫軌跡的overlay


          使用方法:
          LineItemizedOverlay overlay = new LineItemizedOverlay();

          overlay.addOverlay(/*起點(diǎn)的OverlayItem*/);
          overlay.addOverlay(/*終點(diǎn)的OverlayItem*/);
          overlay.addLinePoint(/*要畫的軌跡的GeoPoint的List*/);

          mapView.getOverlays().add(overlay);

          /**
           *
           */
          package com.xtyon.tuola.truck.map;

          import java.util.ArrayList;

          import android.graphics.Canvas;
          import android.graphics.Color;
          import android.graphics.Paint;
          import android.graphics.Point;
          import android.graphics.drawable.Drawable;

          import com.google.android.maps.GeoPoint;
          import com.google.android.maps.ItemizedOverlay;
          import com.google.android.maps.MapView;
          import com.google.android.maps.OverlayItem;
          import com.google.android.maps.Projection;

          /**
           * 地圖上的線型圖層:包括一個(gè)起點(diǎn),一個(gè)終點(diǎn),以及之間的曲線
           * @author superwang
           */
          public class LineItemizedOverlay extends ItemizedOverlay<OverlayItem> {
           private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
             | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG;
           /**
            * 用于保存起點(diǎn)/終點(diǎn)數(shù)據(jù)
            */
           private final ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();

           /**
            * 用于保存構(gòu)成曲線的點(diǎn)的數(shù)據(jù)
            */
           private final ArrayList<GeoPoint> linePoints = new ArrayList<GeoPoint>();

           /**
            * @param defaultMarker
            */
           public LineItemizedOverlay() {
            super(null);

            // TODO Auto-generated constructor stub
           }

           /* (non-Javadoc)
            * @see com.google.android.maps.ItemizedOverlay#createItem(int)
            */
           @Override
           protected OverlayItem createItem(int i) {
            return mOverlays.get(i);

           }

           /* (non-Javadoc)
            * @see com.google.android.maps.ItemizedOverlay#size()
            */
           @Override
           public int size() {
            // TODO Auto-generated method stub
            return mOverlays.size();
           }

           /**
            * 調(diào)價(jià)起點(diǎn)/終點(diǎn)
            * description:
            * @param overlay
            */
           public void addOverlay(OverlayItem overlay) {
            mOverlays.add(overlay);
            populate();
           }

           /**
            * 添加曲線中的點(diǎn)
            * description:
            * @param point
            */
           public void addLinePoint(GeoPoint point) {
            linePoints.add(point);
           }

           public ArrayList<GeoPoint> getLinePoints() {
            return linePoints;
           }

           /**
            * 畫起點(diǎn)/終點(diǎn)/軌跡
            */
           @Override
           public void draw(Canvas canvas, MapView mapView, boolean shadow) {
            if (!shadow) {
             //System.out.println("!!!!!!!!!!!!!!");

             canvas.save(LAYER_FLAGS);
             //canvas.save();

             Projection projection = mapView.getProjection();
             int size = mOverlays.size();
             Point point = new Point();
             Paint paint = new Paint();
             paint.setAntiAlias(true);
             OverlayItem overLayItem;

             //畫起點(diǎn)/終點(diǎn)
             for (int i = 0; i < size; i++) {

              overLayItem = mOverlays.get(i);

              Drawable marker = overLayItem.getMarker(0);
              //marker.getBounds()
              /* 象素點(diǎn)取得轉(zhuǎn)換 */
              projection.toPixels(overLayItem.getPoint(), point);

              if (marker != null) {
               boundCenterBottom(marker);
              }

              /* 圓圈 */
              //Paint paintCircle = new Paint();
              //paintCircle.setColor(Color.RED);
              paint.setColor(Color.RED);
              canvas.drawCircle(point.x, point.y, 5, paint);

              /* 文字設(shè)置 */
              /* 標(biāo)題 */
              String title = overLayItem.getTitle();
              /* 簡(jiǎn)介 */
              //    String snippet = overLayItem.getSnippet();
              //
              //    StringBuffer txt = new StringBuffer();
              //    if (title != null && !"".equals(title))
              //     txt.append(title);
              //
              //    if (snippet != null && !"".equals(snippet)) {
              //     if (txt.length() > 0) {
              //      txt.append(":");
              //     }
              //     txt.append(snippet);
              //    }    
              //Paint paintText = new Paint();

              if (title != null && title.length() > 0) {
               paint.setColor(Color.BLACK);
               paint.setTextSize(15);
               canvas.drawText(title, point.x, point.y, paint);
              }

             }

             //畫線

             boolean prevInBound = false;//前一個(gè)點(diǎn)是否在可視區(qū)域
             Point prev = null;
             int mapWidth = mapView.getWidth();
             int mapHeight = mapView.getHeight();
             //Paint paintLine = new Paint();
             paint.setColor(Color.RED);
             //paint.setPathEffect(new CornerPathEffect(10));
             paint.setStrokeWidth(2);
             int count = linePoints.size();

             //Path path = new Path();
             //path.setFillType(Path.FillType.INVERSE_WINDING);
             for (int i = 0; i < count; i++) {
              GeoPoint geoPoint = linePoints.get(i);
              //projection.toPixels(geoPoint, point); //這一行似乎有問題
              point = projection.toPixels(geoPoint, null);
              if (prev != null) {
               if (point.x >= 0 && point.x <= mapWidth && point.y >= 0 && point.y <= mapHeight) {
                if ((Math.abs(prev.x - point.x) > 2 || Math.abs(prev.y - point.y) > 2)) {
                 //這里判斷點(diǎn)是否重合,重合的不畫線,可能會(huì)導(dǎo)致畫線不在路上
                 canvas.drawLine(prev.x, prev.y, point.x, point.y, paint);
                 //path.lineTo(point.x, point.y);

                 prev = point;
                 prevInBound = true;

                }
               } else {
                //在可視區(qū)與之外
                if (prevInBound) {//前一個(gè)點(diǎn)在可視區(qū)域內(nèi),也需要?jiǎng)澗€
                 //path.lineTo(point.x, point.y);
                 canvas.drawLine(prev.x, prev.y, point.x, point.y, paint);
                }
                prev = point;
                prevInBound = false;
               }
              } else {
               //path.moveTo(point.x, point.y);
               prev = point;

              }
             }
             //canvas.drawPath(path, paint);
             canvas.restore();
             //DebugUtils.showMemory();
            }
            super.draw(canvas, mapView, shadow);
           }

          }

          posted @ 2010-08-12 14:21 王衛(wèi)華 閱讀(1689) | 評(píng)論 (0)編輯 收藏

          tomcat reload時(shí)內(nèi)存泄漏的處理

          我做的應(yīng)用是以Spring為系統(tǒng)的基礎(chǔ)框架,mysql為后臺(tái)數(shù)據(jù)庫(kù).在tomcat上發(fā)布后,總是不能進(jìn)行熱部署(reload),多次reload后,就會(huì)出OutOfMemory PermGen,

          為此煩惱了很久,總于下定決心找找根源.
          經(jīng)過3天的不懈努力,小有成果,記錄下來

          實(shí)際上下面的分析都已經(jīng)沒什么用了,如果你使用tomcat6.0.26及以后的版本,我所說的這些情況都已經(jīng)被處理了,并且比我處理的還要多很多.可以下載tomcat6.0.26的源代碼
          看看WebappClassLoader類的處理就成了.

           

          通過分析工具的分析(用了YourKit,以及JDK1.6/bin下的jps/jmap/jhat),發(fā)現(xiàn)有下面幾個(gè)方面會(huì)造成memory leak.

          1.SystemClassLoader與WebappClassLoader加載的類相互引用,tomcat reload只是卸載WebappClassloader中的class,SystemClassLoader是不會(huì)卸載的(否則其他應(yīng)用也停止了).但是WebappClassloader加載的類被SystemClassLoader引用的化,WebappClassloader中的相關(guān)類就不會(huì)被JVM進(jìn)行垃圾收集

          目前發(fā)現(xiàn)2種容易產(chǎn)生這種leak的現(xiàn)象.
          a.在使用java.lang.ThreadLocal的時(shí)候很容易產(chǎn)生這種情況
          b.使用jdbc驅(qū)動(dòng),而且不是在tomcat中配置的公共連接池.則java.sql.DriverManager一定會(huì)產(chǎn)生這種現(xiàn)象


          ThreadLocal.set(Object),如果這個(gè)Object是WebappsClassLoader加載的,使用之后沒有做ThreadLocal.set(null)或者ThreadLocal.remove(),就會(huì)產(chǎn)生memory leak.
          由于ThreadLocal實(shí)際上操作的是java.lang.Thread類中的ThreadLocalMap,Thread類是由SystemClassLoder加載的.而這個(gè)線程實(shí)例(main thread)在tomcat reload的時(shí)候不會(huì)銷毀重建,必然就產(chǎn)生了SystemClassLoder中的類引用WebappsClassLoader的類.

          DriverManager也是由SystemClassLoder載入的,當(dāng)初始化某個(gè)JDBC驅(qū)動(dòng)的時(shí)候,會(huì)向DriverManager中注冊(cè)該驅(qū)動(dòng),通常是***.driver,例如com.mysql.jdbc.Driver
          這個(gè)Driver是通過class.forName()加載的,通常也是加載到WebappClassLoader.這就出現(xiàn)了兩個(gè)classLoader中的類的交叉引用.導(dǎo)致memory leak.

           

          解決辦法:
          寫一個(gè)ServletContextListener,在contextDestroyed方法中統(tǒng)一刪除當(dāng)前Thread的ThreadLocalMap中的內(nèi)容.
          public class ApplicationCleanListener implements ServletContextListener {

           public void contextInitialized(ServletContextEvent event) {
           }

           public void contextDestroyed(ServletContextEvent event) {
                   //處理ThreadLocal
            ThreadLocalCleanUtil.clearThreadLocals();

            /*
             * 如果數(shù)據(jù)故驅(qū)動(dòng)是通過應(yīng)用服務(wù)器(tomcat etc...)中配置的<公用>連接池,這里不需要 否則必須卸載Driver
             *
             * 原因: DriverManager是System classloader加載的, Driver是webappclassloader加載的,
             * driver保存在DriverManager中,在reload過程中,由于system
             * classloader不會(huì)銷毀,driverManager就一直保持著對(duì)driver的引用,
             * driver無法卸載,與driver關(guān)聯(lián)的其他類
             * ,例如DataSource,jdbcTemplate,dao,service....都無法卸載
             */
            try {
             System.out.println("clean jdbc Driver......");
             for (Enumeration e = DriverManager.getDrivers(); e
               .hasMoreElements();) {
              Driver driver = (Driver) e.nextElement();
              if (driver.getClass().getClassLoader() == getClass()
                .getClassLoader()) {
               DriverManager.deregisterDriver(driver);
              }
             }

            } catch (Exception e) {
             System.out
               .println("Exception cleaning up java.sql.DriverManager's driver: "
                 + e.getMessage());
            }


           }

          }


          /**
           * 這個(gè)類根據(jù)
          */
          public class ThreadLocalCleanUtil {

           /**
            * 得到當(dāng)前線程組中的所有線程 description:
            *
            * @return
            */
           private static Thread[] getThreads() {
            ThreadGroup tg = Thread.currentThread().getThreadGroup();

            while (tg.getParent() != null) {
             tg = tg.getParent();
            }

            int threadCountGuess = tg.activeCount() + 50;
            Thread[] threads = new Thread[threadCountGuess];
            int threadCountActual = tg.enumerate(threads);

            while (threadCountActual == threadCountGuess) {
             threadCountGuess *= 2;
             threads = new Thread[threadCountGuess];

             threadCountActual = tg.enumerate(threads);
            }

            return threads;
           }

           public static void clearThreadLocals() {
            ClassLoader classloader = Thread
              .currentThread()
              .getContextClassLoader();

            Thread[] threads = getThreads();
            try {
             Field threadLocalsField = Thread.class
               .getDeclaredField("threadLocals");

             threadLocalsField.setAccessible(true);
             Field inheritableThreadLocalsField = Thread.class
               .getDeclaredField("inheritableThreadLocals");

             inheritableThreadLocalsField.setAccessible(true);

             Class tlmClass = Class
               .forName("java.lang.ThreadLocal$ThreadLocalMap");

             Field tableField = tlmClass.getDeclaredField("table");
             tableField.setAccessible(true);

             for (int i = 0; i < threads.length; ++i) {
              if (threads[i] == null)
               continue;
              Object threadLocalMap = threadLocalsField.get(threads[i]);
              clearThreadLocalMap(threadLocalMap, tableField, classloader);

              threadLocalMap = inheritableThreadLocalsField.get(threads[i]);

              clearThreadLocalMap(threadLocalMap, tableField, classloader);
             }
            } catch (Exception e) {

             e.printStackTrace();
            }
           }

           private static void clearThreadLocalMap(Object map,
             Field internalTableField, ClassLoader classloader)
             throws NoSuchMethodException, IllegalAccessException,
             NoSuchFieldException, InvocationTargetException {
            if (map != null) {
             Method mapRemove = map.getClass().getDeclaredMethod("remove",
               new Class[] { ThreadLocal.class });

             mapRemove.setAccessible(true);
             Object[] table = (Object[]) internalTableField.get(map);
             int staleEntriesCount = 0;
             if (table != null) {
              for (int j = 0; j < table.length; ++j) {
               if (table[j] != null) {
                boolean remove = false;

                Object key = ((Reference) table[j]).get();
                if ((key != null)
                  && (key.getClass().getClassLoader() == classloader)) {
                 remove = true;

                 System.out.println("clean threadLocal key,class="
                   + key.getClass().getCanonicalName()
                   + ",value=" + key.toString());
                }

                Field valueField = table[j]
                  .getClass()
                  .getDeclaredField("value");

                valueField.setAccessible(true);
                Object value = valueField.get(table[j]);

                if ((value != null)
                  && (value.getClass().getClassLoader() == classloader)) {
                 remove = true;
                 System.out.println("clean threadLocal value,class="
                   + value.getClass().getCanonicalName()
                   + ",value=" + value.toString());

                }

                if (remove) {

                 if (key == null)
                  ++staleEntriesCount;
                 else {
                  mapRemove.invoke(map, new Object[] { key });
                 }
                }
               }
              }
             }
             if (staleEntriesCount > 0) {
              Method mapRemoveStale = map
                .getClass()
                .getDeclaredMethod("expungeStaleEntries", new Class[0]);

              mapRemoveStale.setAccessible(true);
              mapRemoveStale.invoke(map, new Object[0]);
             }
            }
           }
          }

           

          2.對(duì)于使用mysql JDBC驅(qū)動(dòng)的:mysql JDBC驅(qū)動(dòng)會(huì)啟動(dòng)一個(gè)Timer Thread,這個(gè)線程在reload的時(shí)候也是無法自動(dòng)銷毀.
            因此,需要強(qiáng)制結(jié)束這個(gè)timer
           
            可以在 上面的ApplicationCleanListener中加入如下代碼:

              try {
             Class ConnectionImplClass = Thread
               .currentThread()
               .getContextClassLoader()
               .loadClass("com.mysql.jdbc.ConnectionImpl");
             if (ConnectionImplClass != null
               && ConnectionImplClass.getClassLoader() == getClass()
                 .getClassLoader()) {
              System.out.println("clean mysql timer......");
              Field f = ConnectionImplClass.getDeclaredField("cancelTimer");
              f.setAccessible(true);
              Timer timer = (Timer) f.get(null);
              timer.cancel();
             }
            } catch (java.lang.ClassNotFoundException e1) {
             // do nothing
            } catch (Exception e) {
             System.out
               .println("Exception cleaning up MySQL cancellation timer: "
                 + e.getMessage());
            }

           


          3.common-logging+log4j似乎也會(huì)導(dǎo)致leak,看網(wǎng)上有人說在ApplicationCleanListene6中加入這行代碼就可以:
           LogFactory.release(Thread.currentThread().getContextClassLoader());

            我沒試成功,懶得再找原因,直接換成了slf4j+logback,沒有問題.據(jù)說slf4j+logback的性能還要更好.

           

           
          后記:
           tomcat-6.0.26之前的版本(我用的是tomcat-6.0.18),加入上述ApplicationCleanListener后,多次reload,不會(huì)出現(xiàn)outOfMemory.
           但要注意,第一次啟動(dòng)后,reload一次,內(nèi)存會(huì)增加,也就是看著還是由memory Leak,但是重復(fù)reload,內(nèi)存始終保持在第一次reload的大小.似乎tomcat始終保留了雙WebappClassLoader.因此,配置內(nèi)存要小心些,至少要保證能夠load兩倍的你的所有jar包的大小(當(dāng)然,是指Perm的內(nèi)存大小).
           
           測(cè)試過程中最好加上 JVM參數(shù) -verbosegc,這樣,在做GC的時(shí)候可以直觀的看到class被卸載.

           

           

           

           

          posted @ 2010-06-30 18:10 王衛(wèi)華 閱讀(3928) | 評(píng)論 (1)編輯 收藏

          tomcat ssl的配置



          keytool -genkey -alias tomcat -keyalg RSA -keysize 1024 -keypass changeit -storepass changeit -keystore tomcat.keystore -validity 3600
           
          --這兩步可以不用
          keytool -export -trustcacerts -alias tomcat -file tomcat.cer -keystore  tomcat.keystore -storepass changeit
          keytool -import -trustcacerts -alias tomcat -file tomcat.cer -keystore  %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
           
           
          Tomcat4.1.34配置:
          <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"             port="8443" enableLookups="true" scheme="https" secure="true"             acceptCount="100"             useURIValidationHack="false" disableUploadTimeout="true"             clientAuth="false" sslProtocol="TLS"               keystoreFile="tomcat.keystore"               keystorePass="changeit"/> 
          Tomcat5.5.9配置: 
            
          <Connector port="8443" maxHttpHeaderSize="8192"  
                     maxThreads="150" minSpareThreads="25" maxSpareThreads="75"  
                     enableLookups="false" disableUploadTimeout="true"  
                     acceptCount="100" scheme="https" secure="true"  
                     clientAuth="false" sslProtocol="TLS"    
                     keystoreFile="tomcat.keystore"    
                     keystorePass="changeit"/>  
          Tomcat5.5.20配置(此配置同樣可用于Tomcat6.0):
          <Connector protocol="org.apache.coyote.http11.Http11Protocol"    
                               port="8443" maxHttpHeaderSize="8192"  
                     maxThreads="150" minSpareThreads="25" maxSpareThreads="75"  
                     enableLookups="false" disableUploadTimeout="true"  
                     acceptCount="100" scheme="https" secure="true"  
                     clientAuth="false" sslProtocol="TLS"                   
                     keystoreFile="tomcat.keystore"    
                     keystorePass="changeit"/>  
          Tomcat6.0.10配置:
          <Connector protocol="org.apache.coyote.http11.Http11NioProtocol"  
                     port="8443" minSpareThreads="5" maxSpareThreads="75"  
                     enableLookups="true" disableUploadTimeout="true"    
                     acceptCount="100"  maxThreads="200"  
                     scheme="https" secure="true" SSLEnabled="true"  
                     clientAuth="false" sslProtocol="TLS"  
                     keystoreFile="D:/tools/apache-tomcat-6.0.10/tomcat.keystore"    
                     keystorePass="changeit"/>  
             

          其他有用keytool命令(列出信任證書庫(kù)中所有已有證書,刪除庫(kù)中某個(gè)證書):
          keytool -list -v -keystore D:/sdks/jdk1.5.0_11/jre/lib/security/cacerts
          keytool -delete -trustcacerts -alias tomcat  -keystore  D:/sdks/jdk1.5.0_11/jre/lib/security/cacerts -storepass changeit

          posted @ 2009-04-02 15:14 王衛(wèi)華 閱讀(477) | 評(píng)論 (0)編輯 收藏

          DocumentBuilderFactory.newInstance()查找DocumentBuilderFactory實(shí)現(xiàn)類的過程

          1.在系統(tǒng)環(huán)境變量中(System.getProperties())中查找key=javax.xml.parsers.DocumentBuilderFactory
          2.如果1沒有找到,則找java.home\lib\jaxp.properties 文件,如果文件存在,在文件中查找key=javax.xml.parsers.DocumentBuilderFactory
          3.如果2沒有找到,則在classpath中的所有的jar包中查找META-INF/services/javax.xml.parsers.DocumentBuilderFactory 文件
              全都沒找到,則返回null

          posted @ 2009-01-06 13:23 王衛(wèi)華 閱讀(1841) | 評(píng)論 (0)編輯 收藏

          基于角色的訪問控制

          最近公司要做基于角色的訪問控制的模塊,
          自己查了些資料,自己胡亂寫了一些,還不成熟。

          http://www.aygfsteel.com/Files/super/RBAC-權(quán)限管理--設(shè)計(jì)說明書.rar
          http://www.aygfsteel.com/Files/super/RBAC.rar

          posted @ 2007-03-14 16:24 王衛(wèi)華 閱讀(399) | 評(píng)論 (0)編輯 收藏

          主站蜘蛛池模板: 叶城县| 江西省| 思南县| 长治县| 娄烦县| 临城县| 团风县| 天柱县| 昌平区| 军事| 高雄县| 哈尔滨市| 綦江县| 上林县| 洛川县| 喀喇| 山阳县| 昌图县| 菏泽市| 句容市| 锡林郭勒盟| 滁州市| 横峰县| 龙泉市| 岳普湖县| 成安县| 通渭县| 邹平县| 宣化县| 泰安市| 岳普湖县| 申扎县| 乳山市| 会东县| 霍邱县| 临沂市| 铜鼓县| 瓮安县| 巴中市| 宜宾县| 渭源县|