【问题标题】:Managed Memory StringBuilder托管内存 StringBuilder
【发布时间】:2014-02-20 15:45:10
【问题描述】:

问题:将大文件附加到 String 或 StringBuilder 导致 JVM OOME

建议的解决方案:受org.apache.axis.attachments.ManagedMemoryDataSource 类的启发,我编写了一个将 StringBuilder 缓冲区保持在某个最大值的类。当达到该最大值时,该类将 StringBuilder 的内容刷新到磁盘,并且所有后续追加都将完成到磁盘。

是否有任何自定义类可以做到这一点?

我走对了吗?

【问题讨论】:

  • 你有没有尝试过?
  • 是的,实际上我正在实施自定义解决方案。目前它没有实现任何接口。首先我想解决问题,然后我会决定是否符合这些接口中的任何一个是否会给我带来更多好处(现在我认为是,但我不知道我是否有时间)
  • 使用 File.separator 而不是硬编码“/”,否则它在 Unix 中不起作用。
  • 好建议 Lluis。谢谢。现在它更像是学术性而非实用性,但我肯定会在生产版本中使用 File.separator。

标签: java string memory-management


【解决方案1】:

为什么不在您的操作系统中创建更大的虚拟内存并为 JVM 指定更大的堆?看起来像是在尝试重新发明轮子。

【讨论】:

  • 我对大型 Base64 文件有非常高的内存需求,这些文件经常会导致 JVM OOME,因此增加堆只是一种临时措施,直到更大的文件到达然后所有东西都爆炸了。,,
  • 然后是一些 Base64 阅读器,可让您将这些大文件作为流处理。
  • 谢谢 Lluis,请看我上面的代码。我有大的 Base64 文件,也有小的。我不希望磁盘中的所有文件,只有那些可以成为内存瓶颈的文件(例如,在我的代码中超过 100K),所以我想到了一个类的想法,它封装了场景背后的所有逻辑
【解决方案2】:

这是最终版本:

package aytos.util.gestion;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.UUID;    

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

  private static String tmpPath;

  private StringBuilder buffer = new StringBuilder();

  private File tmpFile;

  private static int MAX_BUFFER = 262144;// 256KB

  static
  {
    // get temporal path to store the file
    tmpPath = /* TODO initialize your temporal path here */;
    if(!"".equals(tmpPath))
    {
      tmpPath += File.separator + "ManagedMemoryStringBuilder";
      File folder = new File(tmpPath);
      if(!folder.exists())
      {
        if(!folder.mkdirs())
        {
          tmpPath = "";
        }
      }
    }
  }

  public static void setTmpPath(String tmpPath1)
  {
    tmpPath = tmpPath1;
  }

  public static String getTmpPath()
  {
    return tmpPath;
  }

  public File getFile()
  {
    return tmpFile;
  }

  public ManagedMemoryStringBuilder append(String content)
  {
    if(tmpFile != null)
    {// already on disk
      writeToDisk(content);
    }
    else if(!"".equals(tmpPath) && ((buffer.length() + content.length()) > MAX_BUFFER))
    {// buffer overflow, write to disk
      flushToDisk();
      writeToDisk(content);
    }
    else
    {// great, can store in memory
      buffer.append(content);
    }
    return this;
  }

  public ManagedMemoryStringBuilder append(InputStream is)
  {
    try
    {
      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
      // attemp to fill the whole buffer
      char[] cBuf = new char[MAX_BUFFER - buffer.length()];
      int read = reader.read(cBuf);
      buffer.append(cBuf);

      if(read != -1)
      {// something is left on the reader, so we have to flush the buffer to disk and resume writing
        flushToDisk();
        ReadableByteChannel in = null;
        FileChannel out = null;
        try
        {
          in = Channels.newChannel(is);
          out = new FileOutputStream(tmpFile,true).getChannel();
          ByteBuffer bytebuf = ByteBuffer.allocateDirect(MAX_BUFFER);

          while(in.read(bytebuf) > 0)
          { // Read data from Channel into ByteBuffer
            // flip the buffer which set the limit to current position, and position to 0.
            bytebuf.flip();
            out.write(bytebuf); // Write data from ByteBuffer to file
            bytebuf.clear(); // For the next read
          }
        }
        finally
        {
          if(in != null)
          {
            try
            {
              in.close();
            }
            catch(IOException e)
            {
              // nothing to do
            }
          }
          if(out != null)
          {
            try
            {
              out.close();
            }
            catch(IOException e)
            {
              // nothing to do
            }
          }
        }
      }
      return this;
    }
    catch(IOException e)
    {
      throw new RuntimeException(e);
    }
  }

  public ManagedMemoryStringBuilder append(ManagedMemoryStringBuilder content)
  {
    if(!"".equals(tmpPath) && content.tmpFile != null)
    {// content already on disk
      if(tmpFile == null)
      {// have to tranfer to disk
        flushToDisk();
      }
      try
      {
        FileChannel in = null;
        FileChannel out = null;
        try
        {
          in = new FileInputStream(content.tmpFile).getChannel();
          out = new FileOutputStream(tmpFile,true).getChannel();
          ByteBuffer bytebuf = ByteBuffer.allocateDirect(MAX_BUFFER);

          while(in.read(bytebuf) > 0)
          { // Read data from file into ByteBuffer
            // flip the buffer which set the limit to current position, and position to 0.
            bytebuf.flip();
            out.write(bytebuf); // Write data from ByteBuffer to file
            bytebuf.clear(); // For the next read
          }
        }
        finally
        {
          if(in != null)
          {
            try
            {
              in.close();
            }
            catch(IOException e)
            {
              // nothing to do
            }
          }
          if(out != null)
          {
            try
            {
              out.close();
            }
            catch(IOException e)
            {
              // nothing to do
            }
          }
        }
      }
      catch(IOException e)
      {
        throw new RuntimeException(e);
      }
    }
    else
    {
      append(content.toString());
    }
    return this;
  }

  private void flushToDisk()
  {
    tmpFile = new File(tmpPath + File.separator + UUID.randomUUID());
    writeToDisk(buffer.toString());
    buffer = null;
  }

  private void writeToDisk(String content)
  {
    try
    {
      Writer output = null;
      try
      {
        output = new BufferedWriter(new FileWriter(tmpFile.getCanonicalPath(),true),MAX_BUFFER);
        output.write(content);
      }
      finally
      {
        if(output != null)
        {
          try
          {
            output.close();
          }
          catch(IOException io)
          {
            // Nothing to do
          }
        }
      }
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
  }

  public boolean isFile()
  {
    return tmpFile != null;
  }

  @Override
  public String toString()
  {
    try
    {
      if(isFile())
      {
        FileInputStream fis = new FileInputStream(tmpFile.getCanonicalPath());
        FileChannel fc = fis.getChannel();
        MappedByteBuffer mmb = fc.map(FileChannel.MapMode.READ_ONLY,0,fc.size());
        byte[] buffer str = new byte[(int)fc.size()];
        mmb.get(buffer);
        fis.close();
        fc.close();
        return new String(str);
      }
      else
      {
        return buffer.toString();
      }
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 2016-02-13
    • 2013-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多