java.lang.OutOfMemoryError: Java heap space (JUnit test)

java.lang.OutOfMemoryError: Java heap space (JUnit test)

我有一个 class,我使用输入流然后将其转换为字符串。 我还有一个 Junit 测试,在那里我得到异常“java.lang.OutOfMemoryError:Java heap space”,我知道问题是我在测试中有内存泄漏,但我不明白如何解决 it.Maybe 有人知道怎么做吗?

我的class:

public class IntegrationMonitoringHttpInterceptor implements ClientHttpRequestInterceptor
{

    private static final Logger LOG = LoggerFactory.getLogger(IntegrationMonitoringHttpInterceptor.class);

    @Resource
    private IntegrationMessageService integrationMessageService;

    @Override
    public ClientHttpResponse intercept(final HttpRequest request, final byte[] bytes, final ClientHttpRequestExecution clientHttpRequestExecution) throws IOException
    {
        final String requestMethod = request.getMethod().toString();
        final String requestURI = request.getURI().toString();
        final String requestHeaders = request.getHeaders().toString();
        final String requestBody = new String(bytes, StandardCharsets.UTF_8);
        final String requestMessageBody = requestMethod + "\n" + requestURI + "\n" + requestHeaders + "\n" + requestBody;

        final IntegrationMessageBatchModel batch = saveRequest(requestURI, requestMessageBody);

        try
        {
            final ClientHttpResponse response = clientHttpRequestExecution.execute(request, bytes);

            final String responseStatusCode = response.getStatusCode().toString();
            final String responseStatusText = response.getStatusText();
            final String responseHeaders = response.getHeaders().toString();
            final InputStream responseInputStream = response.getBody();
            final String responseBody = IOUtils.toString(responseInputStream, StandardCharsets.UTF_8.name()); //in the test, a memory leak occurs here
            IOUtils.closeQuietly(responseInputStream);
            responseInputStream.close();
            final String responseMessageBody = responseStatusCode + "\n" + responseStatusText + responseHeaders + "\n" + responseBody;


            saveResponse(responseMessageBody, batch, requestURI);

            return response;
        }
        catch (Exception exception)
        {
            integrationMessageService.createMessageInBatch(exception.getMessage(), batch);
            LOG.warn("Cannot execute request due to unexpected error", exception);
            throw exception;
        }

    }

    private IntegrationMessageBatchModel saveRequest(final String requestURI, final String requestMessageBody)
    {
        try
        {
            IntegrationMessageBatchModel integrationMessageBatchModel = integrationMessageService.createBatch(IntegrationExchangeType.HTTP, requestURI);
            integrationMessageService.createMessageInBatch(requestMessageBody, integrationMessageBatchModel);

            return integrationMessageBatchModel;
        }
        catch (Exception exception)
        {
            LOG.warn("Cannot save message due to unexpected error", exception);
            return null;
        }
    }

    private void saveResponse(final String response, final IntegrationMessageBatchModel batchModel, final String requestURI)
    {
        try
        {
            if (batchModel != null)
            {
                integrationMessageService.createMessageInBatch(response, batchModel);
            }
            else
            {
                IntegrationMessageBatchModel integrationMessageBatchModel = integrationMessageService.createBatch(IntegrationExchangeType.HTTP, requestURI);
                integrationMessageService.createMessageInBatch(response, integrationMessageBatchModel);
            }
        }
        catch (Exception exception)
        {
            LOG.warn("Cannot save message due to unexpected error", exception);
        }
    }
}

我的 JUnit 测试:

@RunWith(MockitoJUnitRunner.class)
@UnitTest
public class IntegrationMonitoringHttpInterceptorTest
{
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Mock
    private IntegrationMessageService integrationMessageService;

    @Mock
    private HttpRequest httpRequest;

    @Mock
    private ClientHttpRequestExecution clientHttpRequestExecution;

    @Mock
    private ClientHttpResponse clientHttpResponse;


    @InjectMocks
    private IntegrationMonitoringHttpInterceptor integrationMonitoringHttpInterceptor = new IntegrationMonitoringHttpInterceptor();

    @Test
    public void intercept() throws Exception
    {

        final String body = "http message";
        final URI uri = new URI("http://headers.jsontest.com/");
        final InputStream inputStream = new InputStream()
        {
            @Override
            public int read()
            {
                return 0;
            }
        };
        when(httpRequest.getURI()).thenReturn(uri);
        when(httpRequest.getMethod()).thenReturn(HttpMethod.POST);
        when(httpRequest.getHeaders()).thenReturn(new HttpHeaders());
        when(clientHttpRequestExecution.execute(httpRequest, body.getBytes(StandardCharsets.UTF_8))).thenReturn(clientHttpResponse);
        when(clientHttpResponse.getStatusCode()).thenReturn(HttpStatus.OK);
        when(clientHttpResponse.getStatusText()).thenReturn("OK");
        when(clientHttpResponse.getHeaders()).thenReturn(new HttpHeaders());
        when(clientHttpResponse.getBody()).thenReturn(inputStream);
        when(integrationMessageService.createBatch(eq(IntegrationExchangeType.HTTP), eq(uri.toString())))
                .thenReturn(new IntegrationMessageBatchModel());
        when(integrationMessageService.createMessageInBatch(eq(body), any(IntegrationMessageBatchModel.class)))
                .thenReturn(new IntegrationMessageModel());
        when(integrationMessageService.createMessageInBatch(contains(body), any(IntegrationMessageBatchModel.class)))
                .thenReturn(new IntegrationMessageModel());

        integrationMonitoringHttpInterceptor.intercept(httpRequest, body.getBytes(StandardCharsets.UTF_8), clientHttpRequestExecution);

        verify(clientHttpRequestExecution, times(1)).execute(httpRequest, body.getBytes(StandardCharsets.UTF_8));
        verify(integrationMessageService, times(1)).createBatch(eq(IntegrationExchangeType.HTTP), eq(uri.toString()));
        verify(integrationMessageService, times(2)).createMessageInBatch(anyString(), any(IntegrationMessageBatchModel.class));
    }

堆栈跟踪:

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2367)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:130)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:114)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:535)
    at java.lang.StringBuilder.append(StringBuilder.java:204)
    at org.apache.commons.io.output.StringBuilderWriter.write(StringBuilderWriter.java:138)
    at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2002)
    at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1980)
    at org.apache.commons.io.IOUtils.copy(IOUtils.java:1957)
    at org.apache.commons.io.IOUtils.copy(IOUtils.java:1907)
    at org.apache.commons.io.IOUtils.toString(IOUtils.java:778)
    at org.apache.commons.io.IOUtils.toString(IOUtils.java:803)
    at core.interceptors.IntegrationMonitoringHttpInterceptor.intercept(IntegrationMonitoringHttpInterceptor.java:48)
    at core.interceptors.IntegrationMonitoringHttpInterceptorTest.intercept(IntegrationMonitoringHttpInterceptorTest.java:85)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)
    at org.junit.rules.RunRules.evaluate(RunRules.java:18)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:69)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:48)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access[=12=]0(ParentRunner.java:50)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222)

您的模拟输入是无限长的 NUL(零)字节流。在某些时候,会尝试将所有内容读入内存,这当然会让你 运行 内存不足,因为它是无限长的:

    final InputStream inputStream = new InputStream()
    {
        @Override
        public int read()
        {
            return 0;
        }
    };

快速解决方法是将其限制为已知大小,例如 100 字节

final InputStream inputStream = new ByteArrayInputStream(new byte[100]);