【问题标题】:Java memory allocation rulesJava内存分配规则
【发布时间】:2021-06-09 13:38:08
【问题描述】:

我遇到了一个问题,但无法理解它发生的原因。我开发自动化测试,现在有一项任务是比较我们内部数据库和供应商数据库中的用户数据计数。

所以我们有大约 8k 用户,我需要比较他们的语句数。

我执行的步骤:

  1. 从 DB 获取用户及其语句计数(使用 HashMap<userId, count> 完成一次)
  2. 为每个 userId 执行 API 调用,获取带有 JSON 对象列表的 JSON 并映射它们(使用 Apache HttpClient)
  3. 将数据库中的计数与映射对象的计数进行比较。

问题:
如果我使用 TestNG 使用 @Test 执行它,在 ~2-3k 请求之后,我在执行 API 调用 (httpClient.execute) 的步骤中收到错误 java.lang.OutOfMemoryError: Java heap space

如果我在 main 方法中使用相同的步骤将它作为类运行,我可以执行超过 8k 个请求而不会出现问题和堆错误。

那么问题来了,运行测试和运行类是否存在内存分配差异?

public class TransactionsCountTest extends BaseTest {

    public static void main(String[] args) {
 int i = 1;
        DatabaseHelper databaseHelper = DatabaseHelper.getInstance();
        HashMap<String, String> users = databaseHelper.getTransactionUserIdsWithCount();
        TransactionsCountTest transactionsCountTest = new TransactionsCountTest();

        for (String user : users.keySet()) {
            System.out.println(i);
            transactionsCountTest.compareStatementsCount(user, users.get(user));
            i++;
        }
}

private void compareStatementsCount(String user, String DbCount) {

        int dbCount = Integer.parseInt(DbCount);
        int vendorCount = 0;

        try {
            logger.info("Checking " + user);
            List<SalesItemShort> venTransactions = Helpers.getCollectionFromResponse(salesDataSteps.getUserSalesData(user).getBody(), SalesItemShort[].class);
            vendorCount = venTransactions.size();
            assertEquals(vendorCount, dbCount);
        } catch (AssertionError e) {
            if (e.getMessage().contains("was NOT")) {
                logger.info("Request timeout for user " + user);
            } else {
                logger.info("Data for user " + user + " is incorrect");
                logger.info("Transactions in DB " + dbCount);
                logger.info("Transactions in Vendor " + vendorCount);
            }
        }
    }

【问题讨论】:

  • 请提供minimal reproducible example,以及您在哪里执行测试。是否在可能内存有限等的环境中?
  • 我在笔记本电脑上执行,两次运行都在同一个环境中完成
  • 您将堆设置为什么?这可能就像一种方式比另一种方式具有更少的堆内存一样简单。
  • 这能回答你的问题吗? Memory issue while running tests through testng

标签: java memory testng heap-memory


【解决方案1】:

类似的问题与早期版本的 TestNG 相关,该问题已根据其发行说明进行了修复。无论如何,增加堆大小应该会有所帮助。

另一种解决方案是使用 InputStreams 本身,而不是将大响应转换为 String,然后再转换为 DTO。 例如,Jackson 可以使用他的映射器将 InputStreams 映射到 DTO。

谢谢!

【讨论】:

    【解决方案2】:

    与通过main() 方法运行相同的测试相比,您在TestNG 中遇到OutOfMemoryError 异常以减少迭代的原因是因为TestNG。

    • TestNG 作为其报告功能的一部分,连接到几个开箱即用的默认侦听器中。
    • 报告侦听器依赖名为 ITestResult 的对象来跟踪每个测试方法的结果。
    • ITestResult 对象在内部为所有已执行的 @Test 方法记住每个 java.lang.reflect.Method 对象。
    • 因此,当您使用 TestNG 运行大量测试时,内存开始膨胀,因为所有这些 java.lang.reflect.Method 引用仍然卡在内存中,并且只有在测试执行完成后才会被刷新。

    您可以执行以下操作来基本上规避其中一些问题,并且仍然不必增加堆大小。

    1. 升级到最新发布的 TestNG 版本(截至今天为 7.4.0)
    2. 通过设置 JVM 参数-Dtestng.memory.friendly=true 使 TestNG 以内存友好的方式运行
    3. 使用参数-usedefaultlisteners=false禁用默认监听器。

    这应该开始导致 TestNG 以内存友好的方式运行(这只是在一定程度上。TestNG 中仍有一些工作要做以进一步减少其内存占用)

    如果您使用 IntelliJ 运行测试,则可以取消选中运行配置中的“使用默认报告器”复选框

    如果你使用的是surefire插件,那么你可以通过属性将usedefaultlisteners设置为false(如下图)

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.0.0-M5</version>
      <configuration>
        <properties>
          <property>
            <name>usedefaultlisteners</name>
            <value>false</value>
          </property>
        </properties>
      </configuration>
    </plugin>
    

    【讨论】:

      猜你喜欢
      • 2015-10-15
      • 1970-01-01
      • 2012-03-01
      • 2013-07-27
      • 2018-07-02
      • 1970-01-01
      • 2013-03-04
      • 1970-01-01
      • 2010-12-26
      相关资源
      最近更新 更多