1

运行以下代码会导致有关 Java 堆空间的异常。

来自 .NET 并掌握了 GC 在那里的工作方式,我想知道在尝试运行以下内容时是否需要考虑内存管理方面的问题:

public static void main(String[] args) throws NumberFormatException, ParseException, IOException {

    Jedis jedis = new Jedis("<HostName>");

    TimeSeriesPoints retrieved = null;

    while(!finished) {

        try {

            finished = true;

            List<String> keys = getNextFiftyKeys();

            String[] cacheKeys = new String[keys.size()];

            List<String> cacheResults = jedis.mget(keys.toArray(cacheKeys));

            List<TimeSeries> cachedTimeSeries = new ArrayList<TimeSeries>();

            for(String cacheResult : cacheResults){
                try {
                    retrieved = TimeSeriesPoints.parseFrom(cacheResult.getBytes());

                    TimeSeries timeSeries = new TimeSeries(retrieved.getName(), retrieved.getPointsList());

                    cachedTimeSeries.add(timeSeries);
                } 
                catch (InvalidProtocolBufferException e) {
                    e.printStackTrace();
                }
            }

            long pointsCount = 0;
            for(TimeSeries timeSeries : cachedTimeSeries){
                pointsCount += timeSeries.points.length;
            }

            System.out.println("retrieved: " + cachedTimeSeries.size());
            System.out.println("points:" + pointsCount);            
    }
}

TimeSeriesPoints.parseFrom引发异常,堆栈跟踪如下。不知道为什么。

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.ArrayList.ensureCapacity(Unknown Source)
    at java.util.ArrayList.add(Unknown Source)
    at com.wimiro.caching.TimeSeriesProtos$TimeSeriesPoints.<init>(TimeSeriesProtos.java:115)
    at com.wimiro.caching.TimeSeriesProtos$TimeSeriesPoints.<init>(TimeSeriesProtos.java:82)
    at com.wimiro.caching.TimeSeriesProtos$TimeSeriesPoints$1.parsePartialFrom(TimeSeriesProtos.java:151)
    at com.wimiro.caching.TimeSeriesProtos$TimeSeriesPoints$1.parsePartialFrom(TimeSeriesProtos.java:1)
    at com.google.protobuf.AbstractParser.parsePartialFrom(AbstractParser.java:141)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:176)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:188)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:193)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:49)
    at com.wimiro.caching.TimeSeriesProtos$TimeSeriesPoints.parseFrom(TimeSeriesProtos.java:958)
    at program.main(program.java:77)

这会导致试图读取 800 个时间序列(每个都有约 4000 个数据点)。因为在这个例子中我一次只处理 50 个时间序列,所以我预计内存占用不会显着增长。

在 .NET 中,我可以毫不费力地做到这一点。是时候向我学习一些Java了。我需要读什么?

4

2 回答 2

1

不幸的是,Protobuf-Java 对重复的原始类型使用了非常低效的编码——每个元素都被装箱。例如 arepeated int32表示为ArrayList<Int>。从理论上讲,这可以在 protobuf 实现中进行优化,但我知道的最后一次并没有这样做。我猜你的问题源于此。

如果您还没有,请尝试增加 JVM 的最大堆大小(例如-Xmx2g2GB)。

于 2014-03-12T10:04:48.303 回答
1

您正在尝试从调用 redis(C char 8 位编码)的 jedis(使用 SafeEncoder 将 UTF-16 转换为 bytearray)获得的 java String(UTF-16)中获取字节数组。我认为这是您问题的根源,您的 java String 可能不正确,这导致 protobuf 失败。

您应该尝试使用 jedis 的字节数组签名:

final List<byte[]> mget = jedis.mget(byteArray1, byteArray2, ...);

然后尝试在字节数组上使用 protobuf。还要检查如何使用 jedis 在 redis 中插入数据,在所有情况下,建议将字节数组签名与二进制数据一起使用。

于 2014-03-10T14:51:09.007 回答