【问题标题】:Managing memory allocated while loading ZipFile管理加载 ZipFile 时分配的内存
【发布时间】:2016-03-29 17:58:42
【问题描述】:

我正在尝试将 69,930 个文件加载到基本文本编辑器中。这一切顺利,在它们全部加载后,内存位于非常酷的 130MB。但是,在加载高峰期间,这可能会达到最大 900MB - 1200MB。

内存全部引用Inflater#buf 字段。这仅用于将文件加载到对象模型中,然后不再使用它并且可以清除字节。

显然,额外的内存在加载后很快就被垃圾收集器清除了——所以没有内存泄漏。但是,似乎没有必要使用这么多额外的内存。

我尝试过的:

  1. 在关闭ZipFile 后立即调用System.gc() 可以“解决”内存问题。这会导致约 75% 的线程监控时间、高 CPU 使用率和缓慢的加载时间。
  2. 减少线程池计数。这减少了影响(至 300MB),但加载时间显着延长。
  3. WeakReference

到目前为止我所拥有的:

我通过一个 4 线程计数的线程池调用负载,每个线程池执行相对简单的任务:

// Source source = ...;
final InputStream input = source.open();

// read into object model

input.close();

在这种情况下,Source 是一个 ZipFileSource,它会读取所有内容:

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class ZipFileSource implements Source {

    private final String file;
    private final String name;

    private volatile ZipFile zip;

    public ZipFileSource(final String file, final String name) {
        this.file = file;
        this.name = name;
    }

    @Override
    public InputStream open() throws IOException {
        close();

        final ZipFile zipFile = new ZipFile(file);
        final ZipEntry entry = zipFile.getEntry(name);

        final InputStream stream = new ZipFileSourceZipInputStream(zipFile.getInputStream(entry));

        this.zip = zipFile;

        return stream;
    }

    @Override    
    public void close() throws IOException {
        if (zip != null) {
            zip.close();
            zip = null;
        }
    }

    private class ZipFileSourceZipInputStream extends InputStream {

        private final InputStream stream;

        ZipFileSourceZipInputStream(final InputStream stream) {
            this.stream = stream;
        }

        @Override
        public int read() throws IOException {
            return stream.read();
        }

        @Override
        public void close() throws IOException {
            ZipFileSource.this.close();
            stream.close();
        }
    }
}

我的想法有点少。我已经决定要么使用本机 zip 提取器,锁定每个 n 请求以进行 System.gc() 调用,要么只是放弃并让它做它的事情。

有没有一种方法可以在内存建立之前更有效地管理内存(需要垃圾回收调用)?

【问题讨论】:

  • 首先创建较小的 zip 文件 :)
  • @EricWang 我实际上正在加载我拥有的整个 Eclipse 库。因为……为什么不呢?
  • 您是否只是想让 jvm 在运行程序时使用更少的内存?还是?
  • @EricWang 我正在尝试“平衡”在加载文件期间发生的内存峰值。之后没有泄漏或内存问题。
  • 在我看来,没有必要。 JVM 不会在一些内存没有使用后立即清除堆,以获得更好的性能。从图中可以看出,minor gcfull gc 由 jvm 自动执行 4 次,以在文件加载期间清理年轻代或老年代。然后你明确地调用了full gc。这不是内存泄漏。 GC会影响Java程序的正常服务,所以延迟到必要时(内存不足之前)。您可以添加更多内存,并在需要时将其分配给 Java 进程。

标签: java multithreading garbage-collection


【解决方案1】:

A) 如果您的应用程序继续运行,它最终会 GC 并在需要内存时收集这些对象。

B) 如果你的应用程序在那个时候完成......好吧......让虚拟机死掉,它会将内存释放回操作系统。

无论如何,没有真正的内存“浪费”。

垃圾收集器的目的是随着时间的推移分摊收集成本。它只能通过将其推迟到将来的某个时间点来做到这一点,而不是像手动管理的语言那样立即尝试free() 一切。

还请注意,您的图表仅显示 已使用 堆(蓝色)正在下降。从操作系统的角度来看,分配的堆(橙色)无论如何都保持不变,因此蓝色图表上的向下倾斜不会为您带来任何好处。

【讨论】:

  • 我改写了标题以更好地反映我想要实现的目标。 A) 首先分配这么多内存是不必要的,所以只保留它是不可接受的。 B)这只是在加载阶段。我从来没有特别说这必须通过使用System.gc() 来解决...我也更愿意以其他方式解决它(也许我错过了一个这样做的库)。橙色线不反映机器分配的实际内存。我只是要删除这个问题并制作我自己的共享缓冲区 zip 系统,因为这里没有人甚至接近主题。
猜你喜欢
  • 2011-12-21
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多