検索

ホームページ  >  に質問  >  本文

java - Tomcat的Spring-MVC项目在重启一段时间后变卡

最近更新了程序之后,发现网页在tomcat重启一阵子之后变得异常的卡。不知道为什么。发现了好多内存泄漏的警告,觉得是不是因为不正常的关闭导致内存不足呢,就试了几个方法。

最先试着把tomcat的context.xml里面设置缓存最大值,貌似设到了100000,启动后发现速度不错,但过了一段时间又卡得不得了了。
再之后把服务器的内存调大了,问题还是照样出现。而且每次系统的缓存只会越来越多,不会减少。

上网看了一些类似问题的回答,有人说是java获取数据库的效率不高而造成的,但是我觉得我这个问题应该不是出在这,因为有很多需要获取数据库的函数代码都变,以前就没有出现过这种问题,为什么现在变成这样呢?不过最近倒是有一些改动,例如添加了两个TimerTask。倒觉得应该问题不回出在这里吧。

package x.xx.xxx;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.annotation.Resource;

import org.apache.log4j.Logger;

import x.xx.xxx.ManagementStation;
import x.xx.xxx.ManagementStationService;
/**
 * 源代码来自《java定时任务,每天定时执行任务》
 * http://www.cnblogs.com/cvst/articles/5818233.html
 *
 */

public class TimerManager {
    @Resource
    RemoteControlController remoteControlController;
    
    @Resource
    ManagementStationService managementStationService;

    @Resource
    ControllerStatusController controllerStatusController;

    // 时间间隔
    private static final long PERIOD_DAY = 24 * 60 * 60 * 1000;
    private static final int START_TIME = 1;

    public void initTimerManager() {
        Calendar calendar = Calendar.getInstance();

        /*** 定制每日1:00执行方法 ***/

        calendar.set(Calendar.HOUR_OF_DAY, START_TIME);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        Date date = calendar.getTime(); // 第一次执行定时任务的时间
        Date date2 = calendar.getTime();

        // 如果第一次执行定时任务的时间 小于 当前的时间
        // 此时要在第一次执行定时任务的时间加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。
        if (date.before(new Date())) {
            date = this.addDay(date, 1);
        }

        Timer timer = new Timer();
        Timer timer2 = new Timer();

        UpdateDailyEletricPowerTimerTask task = new UpdateDailyEletricPowerTimerTask();
        // 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
        timer.schedule(task, date, PERIOD_DAY);

        UpdateLampStatusTimerTask task2 = new UpdateLampStatusTimerTask();
        timer2.schedule(task2, date2, PERIOD_DAY);
    }

    // 增加或减少天数
    public Date addDay(Date date, int num) {
        Calendar startDT = Calendar.getInstance();
        startDT.setTime(date);
        startDT.add(Calendar.DAY_OF_MONTH, num);
        return startDT.getTime();
    }

    public class UpdateDailyEletricPowerTimerTask extends TimerTask {

         private Logger log = Logger.getLogger(UpdateLampStatusTimerTask.class);

        @Override
        public void run() {
            try {
                /**
                 * 查询前昨两天日冻结正向有功总电量
                 */
                Calendar now = Calendar.getInstance();
                int year = now.get(Calendar.YEAR) - 2000;
                int month = now.get(Calendar.MONTH) + 1;
                int day = now.get(Calendar.DATE);
                remoteControlController.dailyPositiveElectricPowerCollecting(year, month, day);

            } catch (Exception e) {
                log.info("-------------NFDFlightDataTimerTask解析信息发生异常--------------");
            }
        }
    }
    
    public class UpdateLampStatusTimerTask extends TimerTask {

         private Logger log = Logger.getLogger(UpdateLampStatusTimerTask.class);

        @Override
        public void run() {
            try {
                /**
                 * 更新全部灯具状态
                 */
                List<ManagementStation> mlist = managementStationService.getManagementStationList();
                StringBuffer temp=new StringBuffer("");
                for(ManagementStation m:mlist) temp.append(m.getMid()+",");
                String[] arr = temp.toString().split(",");
                boolean realTime = false;
                controllerStatusController.UpdateControllerStatus(arr,realTime);

            } catch (Exception e) {
                log.info("-------------UpdateLampStatusTimerTask解析信息发生异常--------------");
            }
        }
    }

不过系统变得死卡死卡的的时候其实是开启thread和进行socket通讯之后。
不过不知道问题出在前置机于客户端的通讯那,还是我这边的系统程序问题,下面是系统接收返回信息的线程,请各位大虾过目过目,谢谢。

private Status<OperationReturnMsgInfo> GetReturnedInfo(int frame_no) throws Exception{
        FrameController.addFrameToSendingQueue(frame_no);
        Status<OperationReturnMsgInfo> status=new Status<OperationReturnMsgInfo>();
        long beginTime = System.currentTimeMillis();
        boolean over_runtime = false;
        /**
         * 如果超过最大接收时间RUNTIME_MAX就直接把接收到的信息返回页面
         * 其他的报文继续在后台进行接收
         */
        while(FrameController.getExpectReutrnedFrameAmount(frame_no) != 0){
            Thread.sleep(1000);
            long nowTime = System.currentTimeMillis();
            over_runtime = nowTime - beginTime > RUNTIME_MAX? true : false;
            if(over_runtime) break;//超时跳出
        }
        List<byte[]> return_frames = FrameController.GetReturnedFrame(frame_no);

        List<OperationReturnMsgInfo> olist= new ArrayList<OperationReturnMsgInfo>();
        if(return_frames !=null){
            for(byte[] return_frame:return_frames){
                List<OperationReturnMsgInfo> olisttmp = parseFrontEndMsg(return_frame);
                if(olisttmp != null)
                    olist.addAll(olisttmp);
            }
            status.setCode(1);
        }else{
            status.setCode(0);
        }
        status.setList(olist);
        
        if(!over_runtime){
            FrameController.RecallFrameNo(frame_no);
        }else{
            /**
             * 后台处理超时没有返回的报文
             */
            final int tmp_frame_no = frame_no;
            new Thread(new Runnable(){

                @Override
                public void run() {
                    int frame_no_in_the_thread = tmp_frame_no;//FIXME 有点问题:如果tmp_frame_no在运行到这行时被其他线程改变了,怎么办?

                    long beginTime = System.currentTimeMillis();
                    boolean over_runtime = false;

                    int lastExpectReutrnedFrameAmount = FrameController.getExpectReutrnedFrameAmount(frame_no_in_the_thread);
                    while(FrameController.getExpectReutrnedFrameAmount(frame_no_in_the_thread) != 0){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        long nowTime = System.currentTimeMillis();
                        over_runtime = nowTime - beginTime > BG_RUNTIME_MAX? true : false;
                        if(over_runtime) {
                            //再超时便强制回收帧序号,虽然暂时和直接回收没有区别。
                            FrameController.CompulsivelyRecallFrameNo(frame_no_in_the_thread);
                            return;
                        }
                        /**
                         * 如果该帧返回报文有继续更新,重置beginTime
                         * 主要用于十分花时间的多报文返回操作RUNTIME_MAX的话就直接返回到页面。
                         * 相当于如果下一个报文返回间隔的时间大于RUNTIME_MAX就强行关闭对该帧的返回报文的接收
                         */
                        if(lastExpectReutrnedFrameAmount != FrameController.getExpectReutrnedFrameAmount(frame_no_in_the_thread)){
                            lastExpectReutrnedFrameAmount = FrameController.getExpectReutrnedFrameAmount(frame_no_in_the_thread);
                            beginTime = System.currentTimeMillis();
                        }
                    FrameController.RecallFrameNo(frame_no_in_the_thread);
                }
                
            }).start();
        }
        return status;
    }
ringa_leeringa_lee2813日前775

全員に返信(2)返信します

  • 迷茫

    迷茫2017-04-18 10:55:08

    質問者は、最初にいくつかの有用な情報を取得するためにいくつかの分析ツールを見つけることができます。
    スタック プログラムの実行が遅い原因として最も考えられるのは、リソース cpu を占有していることです。 cpu

    首先楼主可能使用 jstack pid 打印出线程堆栈,分析一下线程主要是在如何工作,程序运行到哪行代码,在做什么计算?在去逐一排查对应的代码问题

    如果jstack不够楼主还可以使用xrebel/yourkit <ブロック引用>

    まず第一に、投稿者は jstack pid を使用してスレッド スタックを出力し、スレッドが主にどのように動作するか、プログラムがコードのどの行まで実行されるか、どのような計算が実行されているかを分析します。対応するコードの問題を一つずつ確認していきます
    🎜 jstack だけでは不十分な場合は、xrebel/yourkit などのツールを使用して位置決めを支援することもできます🎜 🎜

    返事
    0
  • 巴扎黑

    巴扎黑2017-04-18 10:55:08

    タイトルの説明を見ると、メモリリークによるリソースの取得が原因であるはずです。
    メモリ リークの発見は 1 つの解決策では解決できません。参考となるトラブルシューティングのアイデアを以下に示します。

    1. ヒープ ファイルをリークおよびエクスポートするように Tomcat を設定します

      詳細については、ヒープダンプを生成するように Tomcat を設定する方法を参照してください

    2. ツールを使用してヘッドダンプ ファイルを分析し、異常なスタック情報を特定します。

      一般的な分析ツールには、IBM HeapAnalyzer、jhat、および jmap が含まれます。JMAP ダンプの使用とダンプ ファイルの分析を参照できます。

      コード内の例外スタックの作成と破棄で例外があるかどうかを分析し、正常に破棄されないとリークにつながります
    投稿者は、将来そのような問題に遭遇したときにアナロジーを回避できるように、問題を解決する方法をマスターするよう努めることをお勧めします

    返事
    0
  • キャンセル返事