【问题标题】:Hazelcast endup in heap space out of memory error even if 8GB space allocated即使分配了 8GB 空间,Hazelcast 也会出现堆空间内存不足错误
【发布时间】:2020-06-10 17:42:46
【问题描述】:

我的项目中有一个要求,将 900 万个数据从 oracle 数据库缓存到 Hazelcast。但显然 Hazelcast 消耗的堆空间比它应该消耗的要多。我已经为应用程序分配了 8bg 堆空间,但仍然出现内存不足错误。

下面是我的数据加载器类。

public class CustomerProfileLoader  implements ApplicationContextAware, MapLoader<Long, CustomerProfile> {

private static CustomerProfileRepository customerProfileRepository;

    @Override
    public CustomerProfile load(Long key) {
        log.info("load({})", key);
        return customerProfileRepository.findById(key).get();
    }

    @Override
    public Map<Long, CustomerProfile> loadAll(Collection<Long> keys) {
        log.info("load all in loader executed");
        Map<Long, CustomerProfile> result = new HashMap<>();
        for (Long key : keys) {
            CustomerProfile customerProfile = this.load(key);
            if (customerProfile != null) {
                result.put(key, customerProfile);
            }
        }
        return result;
    }

   @Override
    public Iterable<Long> loadAllKeys() {

        log.info("Find all keys in loader executed");

        return customerProfileRepository.findAllId();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        customerProfileRepository = applicationContext.getBean(CustomerProfileRepository.class);
    }
}

以下是存储库查询。如果我更改以下查询,使其限制为 200 万条数据,那么一切正常。

 @Query("SELECT b.id FROM CustomerProfile b ")
    Iterable<Long> findAllId();

下面是我在hazelcast.xml 文件中的地图配置。在这里,我将backup count 设为zero,在它为 1 之前,但这并没有什么区别。

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast
        xsi:schemaLocation="http://www.hazelcast.com/schema/config
        http://www.hazelcast.com/schema/config/hazelcast-config-3.11.xsd"
        xmlns="http://www.hazelcast.com/schema/config"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- Use port 5701 and upwards on this machine one for cluster members -->

    <network>
        <port auto-increment="true">5701</port>

        <join>
            <multicast enabled="false"/>
            <tcp-ip enabled="true">
                <interface>127.0.0.1</interface>
            </tcp-ip>
        </join>
    </network>

    <map name="com.sample.hazelcast.domain.CustomerProfile">
        <indexes>
            <!-- custom attribute without an extraction parameter -->
            <index ordered="false">postalCode</index>
        </indexes>
        <backup-count>0</backup-count>
        <map-store enabled="true" initial-mode="EAGER">
            <class-name>com.sample.hazelcast.CustomerProfileLoader</class-name>
        </map-store>
    </map>
</hazelcast>

数据库表结构:

ID                   NOT NULL NUMBER(19)        
LOGIN_ID       NOT NULL VARCHAR2(32 CHAR) 
FIRSTNAME              VARCHAR2(50 CHAR) 
LASTNAME               VARCHAR2(50 CHAR) 
ADDRESS_LINE1          VARCHAR2(50 CHAR) 
ADDRESS_LINE2          VARCHAR2(50 CHAR) 
CITY                    VARCHAR2(30 CHAR) 
postal_code                VARCHAR2(20 CHAR) 
COUNTRY                 VARCHAR2(30 CHAR) 
CREATION_DATE  NOT NULL DATE              
UPDATED_DATE   NOT NULL DATE              
REGISTER_NUM          NOT NULL VARCHAR2(10 CHAR) 

其他要点:

  • 我现在只运行一个 hazelcast 服务器实例,其中 分配的堆空间为 8GB JAVA_OPTS=-Xmx8192m。在它是 4GB 之前 但是当我得到堆空间错误时,我增加到 8GB,但没有运气。
  • 暂时在地图被访问时执行 maploader 第一次。
  • 特定表 (customer_profile) 中有 6 列 它没有任何二进制类型。它只有基本的价值观,比如 firstname lastname 之类的。
  • 使用的 hazelcast 版本是 3.8

我现在面临的问题是:

当它获取所有数据并将其加载到映射时,我收到堆空间错误(java.lang.OutOfMemoryError: Java heap space)。现在表中有 900 万条数据。

加载数据也需要很多时间,也许我可以通过运行多个 hazelcast 服务器实例来解决这个问题。

我是 hazelcast 的新手,所以任何帮助将不胜感激:)

【问题讨论】:

  • 该表中一行的平均大小是多少?
  • @StephenC 大约 100 个字节
  • 100 字节代表什么?
  • @StephenC 。你的意思是列的数据类型?
  • @StephenC 我在问题中也添加了数据库表结构。

标签: java hazelcast


【解决方案1】:

在我看来,真正的问题是您有太多数据无法保存在 8GB 堆中。

你说你平均每行有 100 个字节的数据表示为字符串数据。

以下是将 9,000,000 行数据表示为HashMap 所需空间的一些估计值1。假设有 9 个字符串、2 个日期和一个 int

  • 在 64 位 JVM 中,字符串的开销为 48 字节 + 每个字符 2 字节。因此,代表约 100 个字节字符数据的 9 个 Java 字符串总计大约 650 个字节。
  • Date 是 32 字节 x 2 -> 64 字节
  • 代表 9 个字符串、2 个日期和 1 个 int 的记录将是 112 个字节。
  • 密钥(比如Integer)将是 24 个字节。
  • 一个 HashMap 条目将是 40 个字节。
  • (650 + 64 + 112 + 24 + 40) x 9,000,000 -> ~8,000,000,000 字节
  • HashMap 的主数组将是 2^24 x 8 字节 == ~128,000,000 字节

实际数据超过 8GB。然后考虑 Java 堆需要相当多的工作空间这一事实;至少说 30%。

您获得 OOME 一点也不奇怪。我的猜测是您的堆需要大 50% ......并且假设您对每行 100 字节的估计是准确的。


这完全基于您的loadAll 方法,该方法似乎将数据库中的所有行都具体化为常规HashMap。它不考虑 Hazelcast 用于缓存的堆空间或其他内存。

虽然您可以扩展堆,但我认为更改代码以使其不会像那样具体化行会更有意义。目前尚不清楚这是否有意义。这将取决于地图的使用方式。


1 - 我假设您使用的是 Java 8。

【讨论】:

    猜你喜欢
    • 2021-10-25
    • 2014-01-04
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 2012-04-17
    相关资源
    最近更新 更多