최근 메시지 처리 애플리케이션을 Java 7에서 Java 8로 업그레이드했습니다. 업그레이드 이후에 스트림이 읽히는 동안 스트림이 닫혔다는 예외가 때때로 발생합니다. 로깅은 종료 자 스레드가 스트림을 보유하는 객체 (즉 스트림을 닫음)에서 finalize()
을 호출하고 있음을 나타냅니다. 다음과 같이Java에서 강력하게 접근 할 수있는 객체를 호출하는 finalize() 8
코드의 기본 개요는 다음과 같습니다
MIMEWriter writer = new MIMEWriter(out);
in = new InflaterInputStream(databaseBlobInputStream);
MIMEBodyPart attachmentPart = new MIMEBodyPart(in);
writer.writePart(attachmentPart);
MIMEWriter
및 MIMEBodyPart
이 집에서 성장 MIME/HTTP 라이브러리의 일부입니다. MIMEBodyPart
는 다음 갖고, HTTPMessage
확장 :
public void close() throws IOException
{
if (m_stream != null)
{
m_stream.close();
}
}
protected void finalize()
{
try
{
close();
}
catch (final Exception ignored) { }
}
예외는 다음과 같다 MIMEWriter.writePart
의 호출 체인에서 발생 후 part.writeBodyPartContent(this)
를 호출
MIMEWriter.writePart()
이 부분에 대한 헤더를 쓰는MIMEBodyPart.writeBodyPartContent()
우리의 유틸리티 메서드IOUtil.copy(getContentStream(), out)
을 호출하여 출력 내용에 내용을 스트리밍합니다.MIMEBodyPart.getContentStream()
jus t는 컨스트럭터에 전달 된 입력 스트림을 반환합니다 (위의 코드 블록 참조).IOUtil.copy
에는 입력 스트림에서 8K 청크를 읽고 입력 스트림이 비어있을 때까지 출력 스트림에 쓰는 루프가 있습니다.
MIMEBodyPart.finalize()
는 IOUtil.copy
이 실행되는 동안라고하며 다음과 같은 예외를 얻을 수있다 :
java.io.IOException: Stream closed
at java.util.zip.InflaterInputStream.ensureOpen(InflaterInputStream.java:67)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:142)
at java.io.FilterInputStream.read(FilterInputStream.java:107)
at com.blah.util.IOUtil.copy(IOUtil.java:153)
at com.blah.core.net.MIMEBodyPart.writeBodyPartContent(MIMEBodyPart.java:75)
at com.blah.core.net.MIMEWriter.writePart(MIMEWriter.java:65)
우리는 호출자의 스택 트레이스를 기록하고 있음을 입증 HTTPMessage.close()
방법에 약간의 로깅을 넣어 IOUtil.copy()
이 실행되는 동안 HTTPMessage.finalize()
을 호출하는 finalizer 스레드입니다.
MIMEBodyPart
개체는 MIMEBodyPart.writeBodyPartContent
의 스택 프레임에 현재 스레드 스택에서 this
으로 확실히 도달 할 수 있습니다. JVM이 finalize()
이라고하는 이유를 이해할 수 없습니다.
관련 코드의 추출을 시도하고 제 기계에서 엄격한 루프로 실행했지만 문제를 재현 할 수 없습니다. 우리는 dev 서버 중 하나에서 높은 부하로 문제를 신뢰할 수있게 재현 할 수 있지만 재현 할 수없는 작은 테스트 케이스를 만들려는 시도는 실패했습니다. 이 코드는 Java 7에서 컴파일되지만 Java 8에서 실행됩니다. 다시 컴파일하지 않고 Java 7로 다시 전환하면 문제가 발생하지 않습니다.
해결 방법으로, Java Mail MIME 라이브러리를 사용하여 영향을받는 코드를 다시 작성 했으므로 문제가 해결되었습니다 (아마도 Java Mail은 finalize()
을 사용하지 않습니다). 그러나 응용 프로그램의 다른 finalize()
메서드가 잘못 호출되거나 Java가 아직 사용중인 개체를 가비지 수집하려고하는 것이 우려됩니다.
현재 가장 좋은 방법은 finalize()
을 사용하지 말 것을 권장하며,이 자생 라이브러리를 다시 방문하여 finalize()
메서드를 제거합니다. 즉, 이전에이 문제를 만난 사람이 있습니까? 원인에 대한 아이디어가있는 사람이 있습니까?
다른 설명이 없다면 놀랄 것입니다. 현재 스레드는 항상 콜렉터가 활성 객체를 식별하는 "루트"입니다. IOUtils.copy()가 반환되기 전에 파이널 라이저가 호출되는지 * 어떻게 알 수 있습니까? – Bhaskar
이것은 JIT 버그와 매우 비슷합니다. JIT 디버깅을 켜고 패턴이 있는지 여부를 확인합니다. – chrylis
@Bhaskar, 예외는'IOUtil.copy()'가 실행되는 동안 스트림이 닫혔다는 것을 증명합니다. – Nathan