在Android使用MappedByteBuffer写入遇到对齐访问相关问题,SIGBUS报错

各位大神,本人在Android环境使用FTPClient下载文件,之前使用RandomAccessfile进行多线程并发写入,速率较慢,百度一番后使用Mappedbytebuffer,但是有时报 Fatal signal 7 (SIGBUS) at 0x7cc73000 (code=2)错误会直接中断APP,希望大神给予帮助,我的代码中没有操作C/C++,不知道如何实现对齐访问,恳请指点,谢谢

报错的Logcat如下:
12-05 16:24:16.850 10110-10279/com.ftp.instant A/libc: Fatal signal 7 (SIGBUS) at 0x7cc73000 (code=2), thread 10279 (Thread-161)
12-05 16:24:16.950 155-155/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-05 16:24:16.950 155-155/? I/DEBUG: Build fingerprint: 'Android/samsung/samsung:4.4.2/KOT49H/3.7.3.1019:userdebug/test-keys'
12-05 16:24:16.950 155-155/? I/DEBUG: Revision: '0'
12-05 16:24:16.950 155-155/? I/DEBUG: pid: 10110, tid: 10279, name: Thread-161 >>> com.ftp.instant <<<
12-05 16:24:16.950 155-155/? I/DEBUG: signal 7 (SIGBUS), code 2 (BUS_ADRERR), fault addr 7cc73000
12-05 16:24:16.990 155-155/? I/DEBUG: backtrace:
12-05 16:24:16.990 155-155/? I/DEBUG: #00 pc 0003e54a /system/lib/libc.so
12-05 16:24:16.990 155-155/? I/DEBUG: stack:
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7924 00000410

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7928 00000080

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c792c b4ddba92 /system/lib/libdvm.so
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7930 b4ecacb0 /system/lib/libdvm.so
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7934 24b00005

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7938 00000400

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c793c b4d68669 /system/lib/libdvm.so
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7940 b8f7f908 [heap]
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7944 24b00005

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7948 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c794c 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7950 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7954 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7958 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c795c b4ecacb0 /system/lib/libdvm.so
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7960 b8f7f860 [heap]
12-05 16:24:16.990 155-155/? I/DEBUG: #00 7d0c7964 00000400

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7968 b8f7f860 [heap]
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c796c b4d6a70f /system/lib/libdvm.so
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7970 7cc72d54 /storage/emulated/0/ftptmp/ftp5m
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7974 9515dd4c /dev/ashmem/dalvik-heap (deleted)
12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7978 00000400

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c797c 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7980 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7984 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7988 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c798c 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7990 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7994 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c7998 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c799c 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: 7d0c79a0 00000000

12-05 16:24:16.990 155-155/? I/DEBUG: memory map around fault addr 7cc73000:
12-05 16:24:16.990 155-155/? I/DEBUG: 7cac6000-7cc72000 rw- /storage/emulated/0/ftptmp/ftp5m
12-05 16:24:16.990 155-155/? I/DEBUG: 7cc72000-7ce1d000 rw- /storage/emulated/0/ftptmp/ftp5m
12-05 16:24:16.990 155-155/? I/DEBUG: 7ce1d000-7cfc8000 rw- /storage/emulated/0/ftptmp/ftp5m
12-05 16:24:17.040 475-498/system_process I/BootReceiver: Copying /data/tombstones/tombstone_05 to DropBox (SYSTEM_TOMBSTONE)
12-05 16:24:17.040 475-10284/system_process W/ActivityManager: Force finishing activity com.ftp.instant/.MainActivity
12-05 16:24:17.050 475-514/system_process W/InputDispatcher: channel '4a99005c com.ftp.instant/com.ftp.instant.MainActivity (server)' ~ Consumer closed input channel or an error occurred. events=0x9
12-05 16:24:17.050 475-514/system_process E/InputDispatcher: channel '4a99005c com.ftp.instant/com.ftp.instant.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
12-05 16:24:17.070 475-498/system_process D/dalvikvm: GC_FOR_ALLOC freed 654K, 18% free 9730K/11784K, paused 30ms, total 31ms
12-05 16:24:17.080 475-486/system_process W/InputDispatcher: Attempted to unregister already unregistered input channel '4a99005c com.ftp.instant/com.ftp.instant.MainActivity (server)'
12-05 16:24:17.080 475-486/system_process I/WindowState: WIN DEATH: Window{4a99005c u0 com.ftp.instant/com.ftp.instant.MainActivity}

代码在楼下贴出

2个回答


package com.ftp.utils;
import android.text.TextUtils;
import de.greenrobot.event.EventBus;
import com.ftp.bean.CheckEvent;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import rx.Observable;
import rx.Subscriber;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.IllegalFormatCodePointException;

import static com.ftp.utils.Utils.getDownloadfile;
import static com.ftp.utils.Utils.isStop;

/**
* 使用FTPClient下载FTP服务器文件
*/
public class DownloadRunnable implements Runnable {
   private EventBus eventBus;
   private FTPClient ftpClient;
   private int threadno;//当前线程编号
   private int threadnum;//线程总数
   private String remotepath;//远端文件路径
   private MappedByteBuffer bytebuffer;//内存映射缓冲区
   private long bufferMax; //缓冲区允许放置的字节级数据量
   private long currentPos;    //缓冲区中未刷入内存的大小即缓冲区写入模式下的起始位置
   private RandomAccessFile accessFile;    //可随意写入的文件实例
   private FileChannel fileChannel;
   public DownloadRunnable(FTPClient client, int threadno, int threadnum) {
       this.ftpClient = client;
       this.threadno = threadno;
       this.threadnum = threadnum;
       this.remotepath = "/ltedown/ftp5m";
       if (!TextUtils.isEmpty(Utils.getRemotePath()))
           this.remotepath = Utils.getRemotePath();
       this.bufferMax=512*1024;    //设置允许的512KB的缓存数
       setRandomAccessFile();
   }


   @Override
   public void run() {
       try {
           showLog(threadno + "线程 开始下载");
           //在这里计算文件的起始值
           long require_length, start = 0, localsize = 0;//文件起始值,需下载的长度,已下载的长度
           long totalFileSize = Utils.getTotalFileSize();//远端文件的总长度
           require_length = totalFileSize / threadnum;
           start = (threadno - 1) * require_length;
           if (totalFileSize % threadnum != 0)
           {
               if (threadno == threadnum)
                   require_length = require_length + totalFileSize % threadnum;
           }
           ftpClient.setRestartOffset(start);
           InputStream input = ftpClient.retrieveFileStream(remotepath);
           if (input == null) {
               showLog(threadno + "线程无法获取InputStream");
           }
           fileChannel=accessFile.getChannel();
           bytebuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, require_length);
           showLog(threadno + "线程成功获取Map");
           long tmp;
           int length = 0;
           byte[] b = new byte[1024];
           setCurrentPos(0);   //开始写入前设置起始位置为0
           while ((length = input.read(b)) != -1) {
               if (isStop())
                   break;
               if(length==0)
                   showLog(threadno + "线程获取byte数为0");
               if(getCurrentPos()+length>=bufferMax)
               {
                   flush();    //当缓冲区未处理数据达到Max时flush数据
               }
               tmp = localsize + length;
               if (tmp >= require_length)
               {
                   length = (int) (require_length - localsize);
                   bytebuffer.put(b, 0, length);
                   addCurrentPos(length);
                   Utils.addFileSize(length);
                   localsize = localsize + length;
                   break;
               }
               showLog(threadno + "线程 当前tmp大小"+tmp);
               bytebuffer.put(b, 0, length);
               addCurrentPos(length);
               Utils.addFileSize(length);
               localsize = localsize + length;
           }
           input.close();
           showLog(threadno + "线程" + " 下载:" + localsize + "/" + require_length);
           if (!isStop())
               showLog(threadno + "线程" + " 下载完成");
       } catch (Exception e) {
           showLog(threadno + "线程 捕捉到下载异常");
           Utils.ftpRelatedStatus.setDownloadError(true);
           e.printStackTrace();
       } finally {
           try {
               //测试完成在这里作 传出信号的操作且无论下载过程有无异常均上报下载结束
               Utils.addFinish();
               if (accessFile != null) {
                   unmap();    //释放资源,释放前会flush数据
                   fileChannel.close();
                   accessFile.close();
               }
               ftpClient.completePendingCommand();
               ftpClient.logout();
               ftpClient.disconnect();
               showLog(threadno + "线程 结束战斗");
           }
           catch (IOException e) {
               showLog(threadno + "线程 FTPClient释放异常");
               e.printStackTrace();
           }
       }
   }

   private synchronized void flush()
   {
       if(bytebuffer!=null)
           bytebuffer.force();
       setCurrentPos(0);
   }

   private void showLog(String s) {
       Utils.showLog(s);
   }

   /**
    * 显式回收MappedByteBuffer实例
    */
   private void unmap() {
       flush();        //刷入数据
       if(bytebuffer==null)
           return;
       bytebuffer.clear();
       bytebuffer=null;
//        System.gc();
   }

   public void setRemotepath(String remotepath) {
       this.remotepath = remotepath;
   }


   public long getCurrentPos() {
       return currentPos;
   }

   public void setCurrentPos(long currentPos) {
       this.currentPos = currentPos;
   }

   public void addCurrentPos(int variable)
   {
       setCurrentPos(this.currentPos + variable);
   }

   public  synchronized void setRandomAccessFile()
   {
       try {
           accessFile = getDownloadfile(remotepath);
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

}

想问下你后来是怎么解决的?

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问