Java: асинхронная сериализация объекта в файл

#java #performance #asynchronous #serialization #completable-future

#java #Производительность #асинхронный #сериализация #завершаемый-будущее

Вопрос:

Я создаю клон Minecraft, и у меня есть Map<Vector3i, Chunk> called chunks , который хранит все загруженные фрагменты. Каждый кадр выполняется этот цикл:

 for(Vector3i v:chunksToUnrender){
    ...
    CompletableFuture.runAsync(() -> {
        try {
            chunks.get(v).toFile(new File(String.format("res-server/regions/%s.%s.%s.mcr", v.x, v.y, v.z)));
            synchronized (chunksStateGuard) {
                chunks.remove(v);
            }
        } catch(IOException e) {
            e.printStackTrace();
            System.err.println("Unable to save chunk "   Utils.format3i(v));
        }
    });
}
  

Целью здесь является асинхронная выгрузка фрагментов. Содержимое Chunk.toFile(File) является:

 public void toFile(File file) throws IOException {
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(SerializationUtils.serialize(this));
    fos.flush();
    fos.close();
}
  

Однако, несмотря на использование CompletableFuture , всякий раз, когда фрагмент выгружается, игра на короткое время теряет частоту кадров, поскольку сериализует и выгружает фрагмент. Есть ли какой-либо способ избежать прерывания основного потока при выполнении фоновой задачи?

Комментарии:

1. Ввод-вывод файлов требует больших затрат, рассматривали ли вы возможность сериализации часто используемого содержимого в / из кэша в памяти (например, ehCache или Terracota)?

2. @RahulR. Весь смысл сериализации заключается в сокращении потребления памяти. Изначально все фрагменты просто всегда хранились в памяти, но примерно через 1 млн игрового процесса мы получали всплески задержки и ошибки OutOfMemoryErrors.

3. @RahulR. Я не понимаю, что вы имеете в виду. Если то, что вы предлагаете, решит проблему, пожалуйста, опубликуйте ответ.

Ответ №1:

Как обсуждалось в комментариях ранее, вы могли бы дополнительно улучшить реализацию с помощью кэша, который оптимизирует процесс сериализации, обеспечивая при этом более быстрое чтение.

Ниже приведен пример кода, который демонстрирует настройку и использование кэша с использованием ehCache.

Зависимость Maven (внутри pom.xml )

 <dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.9.0</version>
</dependency>
  

Конфигурация ehCache (ehcache.xml )

Примечание — Этот файл должен быть доступен в пределах classpath.

 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">

    <!-- By default, Ehcache stored the cached files in temp folder. -->
    <!-- <diskStore path="java.io.tmpdir" /> -->

    <!-- Ask Ehcache to store cache in this path -->
    <diskStore path="c:\temp\ehcache" />

    <!-- Sample cache named cache1
    This cache contains a maximum in memory of 10000 elements, and will expire
    an element if it is idle for more than 5 minutes and lives for more than
    10 minutes.

    If there are more than 10000 elements it will overflow to the
    disk cache, which in this configuration will go to wherever java.io.tmp is
    defined on your system. On a standard Linux system this will be /tmp" -->
    <cache name="ApplicationContentCache" 
        maxEntriesLocalHeap="10000"
        maxEntriesLocalDisk="1000" 
        eternal="false" 
        diskSpoolBufferSizeMB="20"
        timeToIdleSeconds="300" timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LFU" 
        transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>
  

Различные классы Java

 import java.io.Serializable;
import java.util.concurrent.CompletableFuture;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * Demo class for the caching functionality
 * @author Rahul R
 *
 */
public class ApplicationCacheDemo {

    public static void main(String... args) {
        ApplicationCacheFactory cacheFactory = ApplicationCacheFactory.instance;

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try (ApplicationCache<CachableObject> cache = cacheFactory.getApplicationCache()) {
                CachableObject cacheContent = new CachableObject("A sample content");
                int identity = cache.put(cacheContent);

                CachableObject readContent = cache.get(identity);
                System.out.println(readContent.getData());          
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        future.join();
    }
}

/**
 * The class whose objects would be cached.
 * @author Rahul R
 *
 */

class CachableObject implements Serializable {
    private static final long serialVersionUID = 1L;

    private String data = null;

    public CachableObject() {
        super();
    }

    public CachableObject(String data) {
        super();
        setData(data);
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result   ((data == null) ? 0 : data.hashCode());
        return resu<
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CachableObject other = (CachableObject) obj;
        if (data == null) {
            if (other.data != null)
                return false;
        } else if (!data.equals(other.data))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "CachableObject ["   getData()   "]";
    }
}

/**
 * A singleton factory implemented through Enum
 * @author Rahul R
 *
 */

enum ApplicationCacheFactory {
    instance;

    public ApplicationCache<CachableObject> getApplicationCache() {
        return new ApplicationCache<>("ApplicationContentCache");
    }
}

/**
 * A simplistic cache facade
 * @author Rahul R
 *
 */

class ApplicationCache<E> implements AutoCloseable {    
    private CacheManager cm = null;
    private String cacheName = null;
    private Cache cache = null;

    public ApplicationCache(String cacheName) {
        super();
        setCacheName(cacheName);
        initializeCache();
    }

    private void initializeCache() {
        cm = CacheManager.getInstance();
        cache = cm.getCache(getCacheName());
    }

    public String getCacheName() {
        return cacheName;
    }

    public void setCacheName(String cacheName) {
        this.cacheName = cacheName;
    }

    public int put(E value) {
        int identity = value.hashCode();
        cache.put(new Element(identity, value));
        return identity;
    }

    @SuppressWarnings("unchecked")
    public E get(int identity) {
        E result = null;

        Element element = cache.get(identity);

        if (element != null) {
            result = (E) element.getObjectValue();
        }

        return resu<
    }

    @Override
    public void close() throws Exception {
        cm.shutdown();
    }
}
  

Ответ №2:

Это была проблема X-Y. Проблема заключалась в синхронной загрузке фрагмента из файлов.

Комментарии:

1. Поэтому удалите вопрос. Is здесь больше не используется.