java.lang.OutOfMemoryError: unable to create new native thread
阿神
阿神 2017-04-17 17:50:04
0
3
1050

程序总是在两三天就报一次内存泄漏的异常,不知道是什么原因引起的,因为项目中有一个地方冲到队列,在队列中创建了很多的线程不知道是不是这个引起了。。。

public class GlobalVariables {
    public static ExecutorService pool = Executors.newFixedThreadPool(1000);
    public static BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(1000);
    public static Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    
}

public class AddQueues implements Runnable{
    private Object obj;
    
    public AddQueues(Object obj) {
        this.obj = obj;
    }
    @Override
    public void run() {
        try {
            System.out.println("-------------"+obj);
            GlobalVariables.queue.put(obj);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Override
public Map<String, Object> updateQueuePool(Map<String, Object> map) throws Exception {
        EntranceVisitCountDTO dto = new EntranceVisitCountDTO();
        dto.setAdminUserId(Integer.parseInt(map.get("adminUserId").toString()));
        GlobalVariables.pool.submit(new AddQueues(dto));

        Map<String,Object> param = new HashMap<String,Object>();
        param.put("result", "yes");
        
        return param;
    }

/**
* 定时将队列里的数据放到map中
*/
@Scheduled(cron = "0/3 * * * * ?")
public synchronized void updateVisitCountOfMap(){
    try{
        if(!GlobalVariables.queue.isEmpty()){        
            for(int i = 0; i < 100; i++){
                Object obj = GlobalVariables.queue.take();
                if(obj instanceof EntranceVisitCountDTO){
                    EntranceVisitCountDTO dto = (EntranceVisitCountDTO)obj;
                    Integer visitCount = GlobalVariables.map.get(dto.getAdminUserId());
                    if(visitCount != null && GlobalVariables.map.size() <= 1000){
                        GlobalVariables.map.put(dto.getAdminUserId(), visitCount +1);
                    }else if(GlobalVariables.map.size() > 1000){
                        visitDayCountService.updateEntranceShowCount();
                    }else{
                        GlobalVariables.map.put(dto.getAdminUserId(), 1);
                    }
                }
                if(GlobalVariables.queue.isEmpty()){
                    break;
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
     * 定时将map中的缓存数据同步入口
     */
@Scheduled(cron = "0 0/10 * * * ?")
public synchronized void updateEntranceShowCount(){
    try{
        visitDayCountService.updateEntranceShowCount();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Transactional(rollbackFor = Exception.class)
@Override
public synchronized void updateEntranceShowCount() throws Exception {
        
    if(!GlobalVariables.map.isEmpty()){
        for(Map.Entry<Integer, Integer> entry : GlobalVariables.map.entrySet()){
                
            String day = TimeUtils.getSpecifyDate(0, "yyyy-MM-dd");
            VisitDayCount visitDayCount = (VisitDayCount)visitDayCountDao.getEntranceCountByEntranceInfoIdWithCreateDate(entry.getKey(), day, "VisitDayCount");
            if(null == visitDayCount){
                visitDayCount = new VisitDayCount();
                visitDayCount.setNum(1);
                visitDayCount.setAdminUserId(entry.getKey());
                visitDayCount.setVisitTime(day);
                visitDayCountDao.save(visitDayCount);
            }else{
                visitDayCountDao.updateEntranceVistCount(entry.getKey(), entry.getValue());
            }
                
            GlobalVariables.map.remove(entry.getKey());
        }
    }
}
阿神
阿神

闭关修行中......

모든 응답(3)
Peter_Zhu
  1. 이 코드의 진입점은 updateQueuePool 메서드여야 합니다. 이 메서드는 DTO를 생성한 다음 DTO를 대기열에 넣고 이 대기열의 소비자는 updateVisitCountOfMap입니다. 매 3회마다 초당 100개의 DTO를 소비합니다. updateQueuePooll 메서드 호출 빈도가 소비 빈도보다 크면 조만간 OOM이 발생합니다

  2. GlobalVariables.map에도 비슷한 문제가 있는데 3초마다 최대 100개의 객체를 넣는데 10분마다 소모됩니다.

  3. GlobalVariables의 여러 필드가 공개되어 있어 어떤 코드라도 직접 사용할 수 있다는 의미이기도 합니다.

  4. 메모리 누수가 있는 경우 System.gc를 호출해도 도움이 되지 않습니다.

이 코드는 문제가 많지만 완전한 코드가 아니기 때문에 어디에서 메모리 누수가 발생할지 판단이 불가능합니다. 잠시 동안 코드를 실행한 후 jmap dump를 사용하는 것이 좋습니다. jvm을 다운로드한 다음 virtual vm을 사용하여 파일 덤프를 확인하여 큐에서 참조하는 객체인지 또는 재활용되지 않는 맵의 객체인지 확인합니다.

迷茫

'새 네이티브 스레드를 생성할 수 없습니다'
이런 종류의 OOM은 드물며 코드의 상수 new Thread()로 인해 발생해야 합니다.
스레드 풀로 변경합니다.

小葫芦

설정한 스레드 풀 크기는 1000입니다. 그렇게 많이 구축할 필요는 없습니다. 먼저 호스트의 코어 수와 스레드 수를 낮추는 것이 좋습니다. 또한 jvm의 xxs 할당 크기는 jvm에서 열 수 있는 스레드 수에 영향을 미칩니다. xmlx의 크기를 확인하는 중입니다.

최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿