2010-02-01 2 views
14

매우 많은 양의 트래픽을 처리하는 Java 서버에서 작업하고 있습니다. 서버는 클라이언트 (종종 메가 바이트)의 패킷을 받아들이고 다른 클라이언트로 전달합니다. 서버는 들어오는 패킷이나 나가는 패킷을 명시 적으로 저장하지 않습니다. 그러나 서버는 계속 OutOfMemoryException 예외로 실행됩니다.강제로 Java 오브젝트 강제 삭제

서버의 메시지 전달 구성 요소에 System.gc()을 추가하여 메모리가 해제되기를 바랬습니다. 또한 JVM의 힙 크기를 기가 바이트로 설정했습니다. 나는 아직도 많은 예외를 받고있다.

내 질문은 다음과 같습니다. 메가 바이트 메시지가 무기한으로 대기열에 저장되지 않도록하려면 어떻게해야합니까? 이러한 객체에서 "힙"공간을 사용하지 않도록 "삭제"를 호출 할 수있는 방법이 있습니까?

 try 
     { 
      while (true) 
      { 
       int r = generator.nextInt(100);//generate a random number between 0 and 100 
       Object o =readFromServer.readObject(); 
       sum++; 
       // if the random number is larger than the drop rate, send the object to client, else 
       //it will be dropped 
       if (r > dropRate) 
       { 
        writeToClient.writeObject(o); 
        writeToClient.flush(); 
        numOfSend++; 
        System.out.printf("No. %d send\n",sum); 
       }//if 

      }//while 
     }//try 
+0

여기에 대한 대답 중 일부는 내 참조가 저장하지 않습니다. 서버는 Objects를 전달하는 Socks 프록시입니다. 들어오는 스트림에서 객체를 읽고 나가는 스트림에 쓰는 while 루프가 있습니다. 그게 전부 야. 메모리 프로파일 러를 지금보십시오. –

+2

"Effective Java"의 챕터를 매우 빨리 읽었습니다.) – Bozho

+0

해당 스트림을 닫으시겠습니까? 일부 코드 제공 – Bozho

답변

3

코드를 확인하십시오 : 패킷이 도착하거나 전송 될 때마다 ObjectInput/OutputStream 인스턴스가 새로 생성 되었습니까? 그렇다면 올바르게 닫힙니 까? 그렇지 않다면 각각의 읽기/쓰기 후에 reset()으로 전화하십니까? 객체 스트림 클래스는 참조 된 모든 객체에 대한 참조를 유지하므로 (참조 할 때마다 동일한 객체를 다시 보내지 않도록) 가비지 수집을 방지합니다. 10 년 전 정확한 문제가있었습니다. 실제로 메모리 누수를 진단하기 위해 프로파일 러를 사용해야했던 것은 처음이었습니다 ...

+0

안녕하세요 .. 제가 물어볼 수 있다면, 이것이 이전에 질문했던 질문에 적용 할 수 있습니까? http://stackoverflow.com/ 질문/13488893/java-excel-poi-stop-after-quartz-by-quartz-by-quartz – ides

+0

내 프로그램이 Excel 파일을 쓸 때마다 멈 춥니 다. 출력 스트림이 멈추는 이유가 될 수 있습니다. 감사! – ides

+0

@ides : 아니요, 같은 문제 일 수 없습니다. 코드가 새 출력 스트림을 만들고 나중에 닫습니다. –

3

가비지 수집을 명시 적으로 호출 할 수 없습니다. 그러나 이것은 여기서 문제가되지 않습니다. 아마도이 메시지에 대한 참조를 저장하고있을 것입니다. 처리 된 곳을 추적하고 사용 된 후에도 객체가 객체를 참조하지 않도록하십시오.

이 가장 좋은 방법이 무엇의 더 나은 아이디어를 얻으려면, Effective Java, chapter 2 읽기 - 그것은 JVM이 OutOfMemoryError의 가장자리에있는 경우

+1

실행이 보장되지는 않지만 명시 적 가비지 수집을 호출 할 수 있습니다. –

+0

그렇다면 그것은 명백하지 않습니다. :) – Bozho

+1

흠, 나는 그가 "당신이 명시 적으로 GC를 부를 수있다"고 생각했다. 이 호출은 명시 적이며 GC는 ...;) – TMN

11

, 그것은 GC가 실행됩니다 "만들기 및 개체 파괴"에 관하여이다.

그러므로 System.gc()을 미리 호출하면 문제를 해결할 수 없습니다. 문제는 다른 곳에서 해결됩니다. 기본적으로 두 가지 방법이 있습니다.

  1. 메모리 효율적인 코드를 작성하거나 코드에 메모리 누수를 수정하십시오.
  2. JVM에 더 많은 메모리를 부여하십시오.

을 사용하면 메모리 사용과 잠재적 인 메모리 누수에 대한 많은 정보를 얻을 수 있습니다.

업데이트 :이 문제를 일으키는 코드에 대한 자세한 정보와 편집에 따라, 대신 ObjectInputStream#readUnshared()ObjectOutputStream#writeUnshared()를 사용하는 제안 Geoff Reedy's answer in this topic를 보라. (링크 된) Javadocs는 또한 그것을 잘 설명한다.

+3

개인적으로, 나는 그것들을 재주문한다. :) –

+1

@mmyers, 2는 확실히 쉽지만 누수가있는 경우 조금만 지연하면됩니다.) – Paolo

+0

@mmyers : done :) – BalusC

2

명시 적으로 강제로 삭제할 수는 없지만 하나의 직접 참조를 메모리에 유지 한 다음 참조 객체를 사용하여 가비지 수집 가능 참조를 유지함으로써 메시지에 대한 참조가 유지되지 않도록 할 수 있습니다.

처리 할 메시지에 작은 (한정된 크기의) 큐를 사용한 다음 첫 번째 큐에 공급하는 보조 SoftReference 큐를 사용하는 것은 어떻습니까? 이렇게하면 처리가 진행되지만 메시지가 너무 크면 메모리 오류가 발생하지 않습니다 (이 경우 참조 대기열이 덤프 됨).

1

OutOfMemory 예외가 발생하는 경우 분명히 이러한 개체에 대한 참조가 아직 남아 있습니다. jhat과 같은 도구를 사용하면 이러한 참조가 어디서 붙어 있는지 확인할 수 있습니다.

2

자바에서는 tune 가비지 수집을 할 수 있지만 강제로 사용할 수는 없습니다.

1

필요 이상으로 오래 붙잡고 있는지 확인해야합니다.첫 번째 단계는 사례에 대한 프로파일 러를 가져 와서 힙을보고 오브젝트가 수집되지 않는 이유를 확인하는 것입니다.

JVM 1GB를 지정 했어도 많은 개체가 매우 빠르게 만들어져 이전 세대로 강제로 제거되어 너무 빨리 제거되지 않으면 어린 세대가 너무 작을 수 있습니다.

GC 튜닝에 유용한 정보 : http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html

4

으로 System.gc()는 단지 자바 가상 머신에 추천입니다. 호출하면 JVM이 가비지 콜렉션을 실행하거나 실행하지 않을 수 있습니다.

OutOfMemoryException은 두 가지로 인해 발생할 수 있습니다. 개체에 대한 참조를 유지하거나 많은 패킷을 수락합니다.

첫 번째 경우는 프로파일 러 (profiler)로 분석 할 수 있습니다. 여기서 프로파일이 얼마나 많은 참조가 아직 살아 있는지 확인할 수 있습니다. 메모리 리크를 잘 나타내면 서버의 메모리 소비가 증가합니다. 추가 요청이있을 때마다 Java 프로세스가 조금씩 커지면 참조를 어딘가에 보관할 가능성이 있습니다 (jconsole은 좋은 시작일 수 있습니다)

처리 할 수있는 것보다 많은 데이터를 허용하는 경우 추가 요청을 차단해야합니다 다른 사람들이 완성 될 때까지

1

서버는 클라이언트 (종종 많은 메가 바이트) 및 다른 클라이언트에 전달에서 패킷을 받아들입니다.

코드는 전달하기 전에 "패킷"을 수신합니다. 이것은 완전히 전달 될 때까지 모든 패킷을 저장하기에 충분한 메모리를 필요로한다는 것을 의미하며, 그 패킷이 "많은 메가 바이트 (Megabytes Large)"일 때 실제로 많은 메모리가 필요하다는 것을 의미합니다. 불필요한 대기 시간을 초래합니다.

메모리 누수가있을 수도 있지만 위의 내용이 사실이라면이 "저장 및 전달"디자인이 가장 큰 문제입니다. 앱을 재 설계하여 패킷을 완전히 수신하지 않고 대신 클라이언트에 직접 스트리밍하면 즉, 한 번에 패키지의 작은 부분 만 읽고 클라이언트에 즉시 전송하면 메모리 사용량을 95 %까지 줄일 수 있습니다. 저장 및 전달할 때와 똑같이 클라이언트에게 보이는 방식으로이 작업을 수행하는 것은 어렵지 않습니다.

+0

일반적으로이 디자인을 사용했지만 응용 프로그램에서 "패킷"의 일정 비율을 삭제하는 것이 중요합니다. 양말 프록시 서버 응용 프로그램은 가혹한 네트워크 환경의 환경을 추상화합니다. 나는이 프록시 (각 "패킷"은 LT 코드의 블록이다)를 통해 앞으로 지우는 코딩 스키마를 실행 중이다. –

+0

@Curious George : 테스트를 작성하지 않는 한 왜 패킷을 드롭해야하는지 이해하지 못합니다. 그러나 그렇다고하더라도 특정 패킷이 도착할 때 하나의 패킷을 드롭할지 여부를 결정할 수는 없습니다. 경우 원본 클라이언트에 오류를 반환하거나 단순히 데이터를 저장하지 않고 입력 스트림을 배기? –

0

다른 사람이 여기에 게시 한 것처럼 System.gc를 수동으로 트리거하는 것은 좋은 대답이 아닙니다. 실행이 보장되지는 않으며 서버가 실행되는 동안 오랫동안 서버를 정지시킬 가능성이있는 전체 gc를 트리거합니다 (서버에 1GB의 RAM을 제공하면 1 초 이상, 몇 분 더 큰 시스템에서 오랫동안 멈춤). 확실히 도움이 될 GC를 조정할 수는 있지만 문제를 완전히 고치지는 못합니다.

한 스트림에서 개체를 읽은 다음 다른 개체로 쓰는 경우 전체 개체를 메모리에 보유하고있는 지점이 있습니다. 당신이 진술 한대로이 객체들이 크다면 그것은 당신의 문제 일 수 있습니다. 1 스트림에서 바이트를 읽고 명시 적으로 전체 객체를 보유하지 않고 다른 객체에 쓰기 위해 IO를 다시 작성하십시오 (객체를 검증/검증해야 할 경우 객체 직렬화/직렬화와 함께 작동하는 방법을 볼 수는 없지만).).

0

이전 답글을 모두 추가하기 만하면됩니다. System.gc()는 JVM에서 가비지 수집을 시작하라는 명령이 아닙니다. 이것은 온순한 방향이며 아무 것도 일어나지 않을 것입니다. JVM 명세는 gc 호출에서 수행해야 할 것을 호출하기 위해 벤더들에게 맡긴다. 공급 업체는 아무 것도하지 않을 수도 있습니다!

+0

FWIW, Sun과 IBM JVM 모두에서'System.gc() '를 호출 할 때마다 완료되었습니다. 동기 호출이 아니기 때문에 GC 스레드를 실행 대기열에 놓고 반환한다는 것입니다. 그런 다음 GC 스레드가 실행되고 다시 일시 중단됩니다. 그렇습니다. 기술적으로는 꼭 할 필요는 없으며 확실히 의존하지는 않지만 디버깅 단계로 시도해 볼 가치가 있습니다. 아, 그리고 finalizers도 실행하는 경향이 있습니다. 똑같은 경고지만 똑같은 관찰. – TMN

+0

나는 그것이 신뢰할 수 없다는 것을 말하고 있습니다. Sun과 IBM이이를 구현했을 수도 있습니다. 일부 x 공급 업체가 가지고 있지 않을 수도 있습니다. – Aadith

19

개체 스트림은 개체에서 읽고 쓰는 모든 개체에 대한 참조를 유지합니다. 이것은 직렬화 프로토콜이 스트림의 초기에 나타난 객체에 대한 역 참조를 허용하기 때문입니다. 여전히이 디자인을 사용할 수 있지만 writeObject/readObject 대신 writeUnshared/readUnshared를 사용할 수 있습니다. 나는 이것이 스트림이 물체에 대한 참조를 유지하는 것을 막을 것이라고 확신하지만 확신 할 수 없다.

코 완 (Cowan)이 말한 것처럼 reset() 방법도 여기에 있습니다. 가장 안전한 방법은 을 사용하는 것입니다. ObjectOutputStream에 편지를 쓸 때 reset()이 뒤따라야합니다.

+1

거의 확실하게 대답이기 때문에 충분히 upvote 할 수 없습니다. 여분의 몇 가지 : 네, writeUnshared 당신이 말한대로 정확하게 할 것입니다. 이상하게도 Javadoc에 의해 보장되는 것은 아니지만 Sun의 구현은 분명히 사용자가 설명하는대로 작동합니다. 둘째로, 다른 옵션 (특히 당신이 제한된 역 참조를 원한다면, 서로를 참조하는 객체의 '클러스터'를 작성하지만 그 다음 '클러스터'는 분리됩니다)는 ObjectOutputStream.reset()을 '배치' 이것은 명시 적으로 스트림에 이미 기록 된 객체의 상태를 무시합니다. – Cowan

+0

여기에 내 대답보다 마침내 끝내기 위해 +1이 하나 더 있습니다. – BalusC

+0

@BalusC - 고마워요.) –

0

수신하기 전에 전체 수신 된 패킷을 명시해야한다고 언급 했습니까? 글쎄, 그건 네가 기억에 그것을 모두 저장할 필요가 있다는 뜻은 아니지, 그렇지? 수신 된 패킷을 외부 저장소 (아마도 SSD가 너무 느리면 RAM 디스크 또는 DB)에 저장 한 다음 메모리에 완전히로드하지 않고 직접 수신자에게 파이프하는 것이 실현 가능한 아키텍처 변경입니까?

0

서버가 죽기 전에 적어도 몇 분 동안 실행되면 Visual VM에서 실행 해보십시오. 힙이 얼마나 빨리 성장하고 있으며 어떤 종류의 객체가 있는지에 대해 더 잘 이해할 수 있습니다.

관련 문제