2013-03-18 2 views
0

내 입력 데이터는 하나의 목록에 50,000 (또는 그 이상)의 전자 메일 메시지이며 to, cc 및 bcc의 여러 수신자에게 엄청난 중복이 있습니다. 따라서이 목록에서 고유 한 메시지를 추출해야합니다.스레드, Arraylist 메모리에 미치는 영향 - Java

동일하거나 유사한지를 식별하기 위해 Message의 특정 속성 (From, List 및 Include (String only))을 비교해야합니다.

지금이 50,000 개의 메시지를 50 개의 작은 1000 개의 메시지 목록으로 나누고 스레드의 각 작은 목록의 복제본을 실행합니다.

모든 스레드가 출력을 하나의 목록에 추가하고 마지막으로 해당 스레드의 중복을 검사합니다. JVM이 1.25GB 메모리에 도달하는 동안.

따라서 내가 시도한 후 50,000 개 이상의 메시지를 보내면 메모리 부족 오류가 발생합니다.

removeDeduplicate(array of messages, blank list)이라는 메소드가 있는데, 메시지 배열과 빈리스트를 입력으로 사용하고 그 빈리스트에서 고유 메시지를 리턴합니다. 이 내 코드입니다 : 내가 너무 미세 조정 메모리 향상 및 성능에 대한 내 코드를 시도하고

public Message[] processForDeduplication(Message[] msgs) throws MessagingException, IOException, InterruptedException { 
    final List<Message> output = new ArrayList<Message>(); 

    if(msgs.length < MAX_MSG){ 
     output.addAll(removeDeduplication(msgs, new ArrayList<Message>())); 
    } else { 
     List<Thread> threads = new ArrayList<Thread>(); 
     int index = 0, lastIndex = MAX_MSG; 

     while(index < msgs.length){ 
      if(lastIndex >= msgs.length) { 
       lastIndex = msgs.length; 
      } 
      final Message[] temp = Arrays.copyOfRange(msgs, index, lastIndex); 
      Thread t = new Thread(new Runnable(){ 
       @Override 
       public void run() { 
        try { 
         output.addAll(removeDeduplication(temp, new ArrayList<Message>())); 
        } catch (MessagingException ex) { 
         logger.error(EmailComparator.class.getName() + ex); 
        } catch (IOException ex) { 
         logger.error(EmailComparator.class.getName() + ex); 
        } 
       } 
      }); 
      t.start(); 
      threads.add(t); 
      index = lastIndex; 
      lastIndex = lastIndex + MAX_MSG; 
     } 
     for(Thread t: threads){ 
      while(t.isAlive()){ 
       Thread.sleep(100); 
      } 
     } 
     threads = null; 
    } 
    List<Message> results = removeDeduplication(convertToArray(output), new ArrayList<Message>()); 
    return convertToArray(results); 
} 

. 지금은 5 ~ 6 초가 될 50,000 개의 레코드를 완료하는 데 약 12-15 초가 걸립니다.

+1

당신은 새 스레드마다 루프를 만들 수 있습니다.하나의 스레드를 생성하고 인스턴스를 처리 할 대기열에 추가하십시오. –

+0

나는 아주 비슷한 문제를 여기에서 해결했다 : http://stackoverflow.com/questions/14737541/java-creating-threads-in-a-eventlistener/14737583#14737583 –

+0

@Legend 그러나 그 말은 모든 일을하는 한 개의 실이 느슨한 것을 의미한다. 다중 처리가 필요하며 오랜 시간이 걸릴 것입니다. – bhavin

답변

1

귀하의 Message이 무엇인지 확실하지 않으므로 javax.mail.Message이라고 가정합니다. 내가 지정한대로 메시지의 평등을 검사하는 래퍼 개체를 만들었습니다. 이 개체는 fromto 배열을 Sets로 캐시합니다. 이렇게하면 Set에 O (1) contains 메서드가 있으므로 더 빠른 같음 비교가 가능합니다.
래퍼는 에 의해 다시 계산할 필요가 없도록 hashCode도 캐시합니다.

public static class MessageWrapper { 

    private final Message message; 
    private final Set<Address> from; 
    private final Set<Address> to; 
    private final Object content; 
    private final int hashCode; 

    public MessageWrapper(Message message) throws MessagingException, IOException { 
     this.message = message; 
     this.from = new HashSet<Address>(Arrays.asList(message.getFrom())); 
     this.to = new HashSet<Address>(Arrays.asList(message.getRecipients(Message.RecipientType.TO))); 
     this.content = message.getContent(); 
     this.hashCode = calcHashCode(); 
    } 

    public Message getMessage() { 
     return message; 
    } 

    private int calcHashCode() { 
     int hash = 7; 
     hash = 37 * hash + (this.from != null ? this.from.hashCode() : 0); 
     hash = 37 * hash + (this.to != null ? this.to.hashCode() : 0); 
     hash = 37 * hash + (this.content != null ? this.content.hashCode() : 0); 
     return hash; 
    } 

    @Override 
    public int hashCode() { 
     return hashCode; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     final MessageWrapper other = (MessageWrapper) obj; 
     if (this.from != other.from && (this.from == null || !this.from.equals(other.from))) { 
      return false; 
     } 
     if (this.to != other.to && (this.to == null || !this.to.equals(other.to))) { 
      return false; 
     } 
     if (this.content != other.content && (this.content == null || !this.content.equals(other.content))) { 
      return false; 
     } 
     return true; 
    } 
} 

저장소에 가장 비싼 것은 정말입니다 content - 만 content.hashCode를 저장하고 그 비교 고려할 수 있습니다; 그러나 이것은 충돌을 허용 할 것입니다.

이제 MessageWrapper들로 모든 Message들 투입 할과 HashSet에 그것을 넣어 할 필요가있는 -이 자동으로 변환해야 그것은 조금 지저분한의 equals()

public Message[] processForDeduplication(final Message[] messages) throws MessagingException, IOException { 
    final Set<MessageWrapper> messageWrappers = new HashSet<MessageWrapper>(messages.length, 1.0f); 
    for (final Message m : messages) { 
     messageWrappers.add(new MessageWrapper(m)); 
    } 
    final List<Message> ms = new ArrayList<Message>(messages.length); 
    for (final MessageWrapper wrapper : messageWrappers) { 
     ms.add(wrapper.getMessage()); 
    } 
    return ms.toArray(new Message[messages.length]); 
} 

있는 항목을 삭제합니다 끝에있는 Message[]으로 다시 돌아갑니다.

분명히 Messagejavax.mail.Message이 아닌 경우 구현이 다를 수 있습니다. 문제의 클래스에 직접 equalshashCode을 구현할 수도 있습니다.

+0

코드 bmorris591을 시도했습니다. 그리고 HASH가 제대로 작동하지 않을 것으로 예상했기 때문에. 그것은 서로 다른 해시 원수를 생성했기 때문에 결코 작동하지 않습니다. 그러나 시간과 도움에 감사드립니다. – bhavin

+1

글쎄, 내가 말했듯이 - 당신의 필요에 맞게 수정해야한다. ** 정확하게 ** 맞아야한다. 2-3 개의 메시지로 간단한 테스트 케이스를 작성하고 메소드를 적절하게 조정하십시오. –

0

@Perception & @David 당신이 제안한대로 시도해 보니 코드는 다음과 같습니다. 상황이 더욱 악화되어 현재 20,000 개 밖에 없으므로 OutOf 메모리 예외가 발생합니다.

public Message[] processForDeduplicationNew(Message[] msgs) throws MessagingException, IOException, InterruptedException { 
    TreeSet<Message> output = new TreeSet<Message>(new Comparator<Message>(){ 

     @Override 
     public int compare(Message msg1, Message msg2) { 
      try { 
       List<String> newRecipients = getRecipients(msg1.getAllRecipients()); 
       List<String> recipients = getRecipients(msg2.getAllRecipients()); 

       if (newRecipients.size() == recipients.size()) { 
        for (int i = 0; i < recipients.size(); i++) { 
         if (!StringUtils.equalsIgnoreCase(newRecipients.get(i), recipients.get(i))) { 
          return 1; 
         } 
        } 
       } else { 
        return 1; 
       } 

       if (!StringUtils.equalsIgnoreCase(((InternetAddress) msg1.getFrom()[0]).getAddress(), ((InternetAddress) msg2.getFrom()[0]).getAddress())) { 
        return 1; 
       } 

       BodyPart newMsgBody= ((Multipart) msg1.getContent()).getBodyPart(0); 
       BodyPart msgBody = ((Multipart) msg2.getContent()).getBodyPart(0); 

       if (!StringUtils.equalsIgnoreCase((String) newMsgBody.getContent(), (String) msgBody.getContent())) { 
        return 1; 
       } 
      } catch (MessagingException ex) { 
       java.util.logging.Logger.getLogger(EmailComparator.class.getName()).log(Level.SEVERE, null, ex); 
      } catch (IOException ex) { 
       java.util.logging.Logger.getLogger(EmailComparator.class.getName()).log(Level.SEVERE, null, ex); 
      } 
      return 0; 
     } 

    }); 

    for (Message message : msgs) { 
     output.add(message); 
    } 

    return output.toArray(new Message[0]); 
} 

더 이상의 아이디어 .... 감사

관련 문제