【问题标题】:Heap Memory Issues on JavaJava 的堆内存问题
【发布时间】:2020-02-02 09:56:38
【问题描述】:

我正在尝试运行下面的程序,但在 StringBuilder 附加循环结构期间出现 OutOfMemory 错误。

  1. 我正在尝试做任何事情来降低内存使用量,使其能够读取 CSV 文件(超过 200,000 行但只有 3 列:项目、评级、用户)。
  2. 然后我将创建一个 2D int 数组,其中唯一项表示 行,唯一用户代表列,交集是 评分。
  3. 最后,我将使用 StringBuilder 来帮助创建输出 CSV 文件

感谢您的帮助和时间。

      List<String> userList = new ArrayList<String>();
      List<String> itemList = new ArrayList<String>();


      FileInputStream stream = null;
      Scanner scanner = null;
      int[][] layout = new int[10672][24303];

      int indexItemList = 0;
      double temp = 0;

      try{
         stream = new FileInputStream(fileName);
         scanner = new Scanner(stream, "UTF-8");
         while (scanner.hasNextLine()){
             String line = scanner.nextLine();
             if (!line.equals("")){
                String[] elems = line.split(",");
                if (indexItemList == 0) {
                    temp = Double.valueOf(elems[1]);
                  layout[0][0] = (int)temp;
                    itemList.add(elems[0]);
                    userList.add(elems[2]);
                    indexItemList++;
                }
                else {
                    boolean itemFound = itemList.contains(elems[0]);
                    boolean userFound = userList.contains(elems[2]);

                    int indexItem = 1;
                    int indexUser = 1;
                    if ((itemFound) && (userFound)) {
                        indexItem = itemList.indexOf(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                    }                    
                    else if ((itemFound) && (!userFound)) {
                        userList.add(elems[2]);
                        indexItem = itemList.indexOf(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                    }
                    else if ((!itemFound) && (userFound)){
                        itemList.clear();
                        itemList.add(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItemList][indexUser] = (int)temp;
                        indexItemList++;
                    }
                    else if (!((itemFound) && (userFound))) {
                        itemList.clear();
                        itemList.add(elems[0]);
                        userList.add(elems[2]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                        indexItemList++;
                    }   
                }
             }
         } 
         if (scanner.ioException() != null){
            throw scanner.ioException();
         }
      }
      catch (IOException e){
         System.out.println(e);
      }
      finally{
         try{
            if (stream != null){
               stream.close();
            }
         }
         catch (IOException e){
            System.out.println(e);
         }
         if (scanner != null){
            scanner.close();
         }
      }

      StringBuilder sb = new StringBuilder();

      for (int i = 0; i < layout.length; i++){
          for (int j = 0; j < layout[i].length; j++){
             sb.append(layout[i][j] + "");
             layout[i][j] = 0;
             if (j < layout[i].length - 1){
                sb.append(",");
             }
          }
          sb.append("\n");
       }

【问题讨论】:

  • 我觉得你不需要StringBuilder:你可以直接写入输出文件。
  • 您需要的最终 CSV 是什么?您是否尝试为每个独特的用户/项目组合写出一个值?要减少字符串生成器的内存占用,请频繁写入文件,而不是尝试将整个 csv 保存在内存中。

标签: java csv memory-management out-of-memory heap-memory


【解决方案1】:

您的文件有 200'000 行,但您的二维数组有 259'361'616 个单元格,StringBuilder 的大小将与该数字成正比。您不需要存储所有这些:它是一个非常空心的矩阵。

这是我要做的:在读取输入文件时,我会构建两个字符串集:项目和用户,以及将评分与每个(项目、用户)对关联的映射:

    Set<String> items = new TreeSet<>();
    Set<String> users = new TreeSet<>();
    Map<String,Double> ratings = new HashMap<>();
    try (InputStream stream = new FileInputStream(fileName);
            Scanner scanner = new Scanner(stream, "UTF-8")) {
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            if (!line.equals("")) {
                String[] elems = line.split(",");
                String item = elems[0];
                String user = elems[2];
                double rating = Double.parseDouble(elems[1]);
                items.add(item);
                users.add(user);
                ratings.put(item+','+user, rating);
            }
        }
    } catch (IOException e) {
        System.out.println(e);
    }

请注意,我使用TreeSets 来确保元素已排序,但如果您不关心这一点,您可以使用HashSets 代替。为了保持元素的出现顺序,就像你在代码中做的那样,你可以使用LinkedHashSets。

然后您可以像这样写入输出文件:

    try (OutputStream stream = new FileOutputStream(outputName);
            Writer writer = new OutputStreamWriter(stream, "UTF-8");
            PrintWriter out = new PrintWriter(writer)) {
        for (String item: items) {
            int j = 0;
            for (String user: users) {
                Double rating = ratings.get(item+','+user);
                double r = rating == null ? 0 : rating;
                out.print(r);
                ++j;
                if (j < users.size()) {
                    out.print(',');
                }
            }
            out.println();
        }
    } catch (IOException e) {
        System.out.println(e);
    }

更新:

如果您对同一对(项目、用户)有多个评分,您只保留最后一个。您可以在地图中使用 Accumulators 而不是 Doubles 来计算平均值:

public class Accumulator {
    private int count;
    private double sum;

    public void add(double value) {
        sum += value;
        ++count;
    }

    public double getAverage() {
        return count == 0 ? 0 : sum/count;
    }
}

更新 2:勘误表

StringBuilder 的大小与矩阵的大小不成正比,而是与项目数乘以用户数成正比。

【讨论】:

    【解决方案2】:

    The Structure of the Matrix I Am Trying to Create

    我正在解析的原始 CSV 文件超过 200,000 行,正好有 3 列(用户、评级、项目)。我希望创建一个类似于所附照片的矩阵。原因是我打算稍后使用这个矩阵来计算两个矩阵行之间的余弦相似度(这将是该方法的输入)。基本上,我会将第一行 (userID #1) 的所有评分与第二行 (userID #2) 的评分进行比较。

    【讨论】:

      猜你喜欢
      • 2012-04-27
      • 2011-11-26
      • 2018-03-11
      • 2021-04-19
      • 2016-02-12
      • 1970-01-01
      • 2021-12-24
      • 2016-02-22
      • 1970-01-01
      相关资源
      最近更新 更多