java - 多线程时出现 OutOfMemory 异常...堆空间?

标签 java android multithreading out-of-memory heap-memory

我正在开发一个需要与 Android 2.3 (Gingerbread) 兼容的应用程序,我用于开发测试的设备是运行 Android 2.3.6 的 Motorola Atrix MB860。

在此设备中,我获得了大约 40MB 的最大堆空间,据我所知,我的应用程序使用了大约 33MB,但无论如何我都会遇到 OutOfMemoryError 异常。

基本上,我的代码中与此问题相关的部分创建了一个大的String(8MB - 我知道它相当大,但如果它太小,则无法满足其中一个要求)然后继续创建2个线程,使用该字符串并发写入某个内存空间。

这是代码:

    // Create random string
    StringBuilder sb = new StringBuilder();
    sb.ensureCapacity(8388608); // ensuring 8 MB is allocated on the heap for the StringBuilder object
    for (long i = 0; i < DATA_SIZE; i++) {
            char c = chars[new Random().nextInt(chars.length)];
            sb.append(c);
    }
    String randomByteString = sb.toString();

    ExecutorService executor = Executors.newFixedThreadPool(2);
    for (int i = 0; i < 2; i++) {
        Runnable worker = new SlidingBubbles(param1, param2, randomByteString)
        executor.execute(worker);
    }
    // This will make the executor accept no new threads 
    // and finish all existing threads in the queue 
    executor.shutdown();

    // Wait until all threads are finish 
    while(!executor.isTerminated()) {
        // wait for bubble threads to finish working...
    }

以及线程的例程:

private class SlidingBubbles implements Runnable {
    private int param1, param2;
    private String randomByteString;
    private final Object mSignal = new Object();
    private volatile long tempBytesWritten = 0;
    private volatile long totalBytesWritten = 0;

    public SlidingBubbles(int param1, int param2, String randomByteString) {
        this.param1= param1;
        this.param2= param2;
        this.randomByteString = randomByteString;
    }

    private void doIt() {
        File file = null;
        RandomAccessFile randomAccessFile = null;
        FileChannel fc = null;

        try {
            while(param1> 0) {
                // Instantiate the 1st bubble file
                file = new File(TARGET_DIR, String.valueOf(Calendar.getInstance().getTimeInMillis()));

                while(param2 > 0) {                        
                    randomAccessFile = new RandomAccessFile(file, "rwd");
                    fc = randomAccessFile.getChannel();

                    fc.position(fc.size());
                    synchronized (mSignal) {
                        tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));

                        totalBytesWritten += tempBytesWritten;
                    }

       // some other things that don't matter
    }

    @Override
    public void run() {
        wipe();            
    }
}

对我来说,尴尬的是,在线程例程的第 30 行 (tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));),第二个线程 ("pool-1-thread-2”)启动异常,退出,第一个线程(“pool-1-thread-1”)继续(实际上,开始)正常执行。 当 JVM 完成为该大 String 分配空间时,应用程序正在使用 33MB 的堆。正如您从代码中看到的,String 仅创建一次,但随后在两个线程中多次使用。

线程不应该只使用对String的引用而不是复制它吗? (在这种情况下,这将超过 40MB 的限额)。

我还必须指出,增加 Gingerbread ( previous research ) 上的堆空间是不可能的(或者至少看起来是,据我的理解)。

我有什么遗漏的吗? 非常感谢任何帮助。

最佳答案

您可以静态拥有 8MB 数据一次,并且永远不会创建它的副本。在 Android 上,StringBuilderString 共享内部 char[],但 String#getBytes() 创建一个副本每次的数据。

我假设您的字符是纯 ASCII,当它们更特殊时,这无法正常工作。

Random random = new Random(); // once!
byte[] data = new byte[8388608];
for (int i = 0; i < data.length; i++) {
    data[i] = (byte) chars[random.nextInt(chars.length)];
}

上面将创建一次数据而无需复制。另请注意 new Random() 8388608?循环中的次数也会导致大量内存使用,但它们应该很快被垃圾收集。

当你这样做时

    public SlidingBubbles(int param1, int param2, byte[] data) {
        ...
        synchronized (mSignal) {
            tempBytesWritten = fc.write(ByteBuffer.wrap(data));

您不再创建该数据的副本,ByteBuffer.wrap 不会创建该数据的副本。无论您做什么,请将完成的 byte[] 传递给 SlidingBubbles

P.s: while(!executor.isTermminate()) { 是错误的方法,有一个方法:How to wait for all threads to finish, using ExecutorService?

关于java - 多线程时出现 OutOfMemory 异常...堆空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27194708/

相关文章:

java - 如何更改 IntelliJ Java 最大堆大小?

java - Android - 在多个 Activity 中访问数组

Android TextView 在 Jelly Bean 上不可见

multithreading - std::map 在 c++11 中插入线程安全吗?

java - 由synchronized关键字标记的关键部分与使用信号量有何不同?

c++ - boost::condition_variable::notify_one() 的行为

java - System.arraycopy问题

java - Apache Hadoop 没有做它应该做的事情,而是合并和减少我的程序中的工作

java - 在 Hibernate Java 中获取两个日期之间的用户数据

android - Android:在手机上处理视频和声音