2024年鸿蒙最新如何解决写入放大效应导致的内存问题(4),中铝2024不笔试直接面试么

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

SharedPreferences sp = context.getSharedPreferences("user_login", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("password", newPassword);
editor.apply();

将整个SharedPreferences文件读入内存中,然后修改密码值,最后将整个文件重新写回磁盘中,导致了内存写入放大问题。

解决方式

可以使用FileOutputStream对应的FileChannel进行文件操作,这样可以将修改后的值直接写入文件,而不需要读取整个SharedPreferences文件

File file = new File(context.getFilesDir(), "user_login.xml");
try (FileOutputStream fos = new FileOutputStream(file);
     FileChannel channel = fos.getChannel()) {
    String xml = "<map><string name=\"username\">" + username + "</string><string name=\"password\">" + newPassword + "</string></map>";
    ByteBuffer buffer = ByteBuffer.wrap(xml.getBytes());
    channel.write(buffer);
} catch (IOException e) {
    e.printStackTrace();
}


在使用文件操作时导致内存写入放大问题
问题

假设有一个记录用户日志的文件,每条日志记录占用256字节,现在需要修改其中一条记录的内容,可以使用以下代码实现:

RandomAccessFile raf = new RandomAccessFile("user.log", "rw");
byte[] buffer = new byte[256];
raf.seek(recordOffset);
raf.read(buffer);
// 修改buffer中对应记录的内容
raf.seek(recordOffset);
raf.write(buffer);
raf.close();

这段代码会将整个256字节块读入内存中进行修改,然后重新写回文件中,导致了内存写入放大问题。

解决方式

可以使用内存映射文件的方式来避免内存写入放大问题。例如,可以使用MappedByteBuffer对文件进行操作,该类可以将文件映射到内存中,并且只有在需要写回磁盘时才会进行写操作。以下是修改用户日志记录的示例代码

RandomAccessFile raf = new RandomAccessFile("user.log", "rw");
FileChannel channel = raf.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, recordOffset, 256);
// 修改map中对应记录的内容
map.force(); // 强制将修改写回磁盘
channel.close();
raf.close();


文件读写时还可能遇到读写次数过多问题,这个我在Bitmap 解码优化中提到过,可以看看,非常有优化价值。

在使用Bitmap进行操作时导致内存写入放大问题
问题

假设有一个需要处理大量图片的应用,现在需要对一张图片进行裁剪操作,可以使用以下代码实现:

Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, x, y, width, height);
FileOutputStream fos = new FileOutputStream(outputPath);
croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
fos.close();


这段代码会将整张图片读入内存中进行裁剪操作,然后将裁剪后的图片重新写回磁盘中,导致了内存写入放大问题。

解决方式

使用BitmapRegionDecoder对图片进行裁剪,该类可以只解码出需要的图片区域,并且不会将整张图片读入内存中。以下是使用BitmapRegionDecoder实现裁剪的示例代码:

InputStream is = new FileInputStream(imagePath);
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
Bitmap croppedBitmap = decoder.decodeRegion(new Rect(x, y, x + width, y + height), null);
FileOutputStream fos = new FileOutputStream(outputPath);
croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
fos.close();


这段代码会使用BitmapRegionDecoder只解码出需要的图片区域,然后将裁剪后的图片写回磁盘中,避免了内存写入放大问题。

在使用IO流进行操作时导致内存写入放大问题
问题

假设有一个需要将数据写入文件的应用,现在需要写入一个大文件,可以使用以下代码实现:

File inputFile = new File(inputPath);
File outputFile = new File(outputPath);
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
byte[] buffer = new byte[1024 * 1024]; // 1MB buffer
int len;
while ((len = fis.read(buffer)) != -1) {
    fos.write(buffer, 0, len);
}
fis.close();
fos.flush();
fos.close();


这段代码会将大文件分成若干个1MB大小的块,然后将每个块读入内存中进行写入操作,导致了内存写入放大问题。

解决方式

可以使用FileChannel进行文件操作,该类可以将文件映射到内存中,并且支持直接对内存进行操作,而不需要将数据读入内存中。以下是使用FileChannel实现文件写入的示例代码:

File inputFile = new File(inputPath);
File outputFile = new File(outputPath);
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024); // 1MB buffer
while (inChannel.read(buffer) != -1) {
    buffer.flip();
    outChannel.write(buffer);
    buffer.clear();
}
inChannel.close();
outChannel.force(true); // 强制将修改写回磁盘
outChannel.close();
}
}

在使用数据库进行操作时导致内存写入放大问题
问题

假设有一个需要使用SQLite数据库进行操作的应用,现在需要插入大量数据到数据库中,可以使用以下代码实现:

SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
    for (int i = 0; i < count; i++) {
        ContentValues values = new ContentValues();
        values.put("column1", value1);
        values.put("column2", value2);
        values.put("column3", value3);
        db.insert("table", null, values);
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}


这段代码会将大量数据插入到数据库中,导致了内存写入放大问题

解决方式

可以使用SQLiteDatabase的insertWithOnConflict()方法插入多条数据,该方法可以将多条数据插入到数据库中,而不需要将所有数据读入内存中。以下是使用insertWithOnConflict()方法插入多条数据的示例代码:

SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
    for (int i = 0; i < count; i++) {
        ContentValues values = new ContentValues();
        values.put("column1", value1);
        values.put("column2", value2);
        values.put("column3", value3);
        db.insertWithOnConflict("table", null, values, SQLiteDatabase.CONFLICT_REPLACE);
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}


使用insertWithOnConflict()方法将多条数据插入到数据库中,避免了内存写入放大问题。

其他问题
  • 在使用反射进行操作时,频繁地创建Class对象
  • 在使用多个线程进行操作时,使用同步锁进行线程同步
  • 在使用大量字符串进行操作时,将多个字符串拼接为一个字符串
  • 在使用大量字符串进行操作时导致内存写入放大问题(循环拼接等)
  • 在使用网络通信进行操作时导致内存写入放大问题(网络中获取到的数据全部读入内存中,然后再写入到输出流中就会导致此问题出现,可以使用Java的NIO的FileChannel 将数据从输入流直接写到输出流中,当然网络在Android中已经不需要我们过分关心了)
  • 其他问题(遇到或听到会补充)

总结

大家有没有发现,这类问题都是平常写代码中遇到的问题,并且有的问题还是高频出现的,并且大家都知道这个问题,就像当初第一次听到内存抖动时大家都很懵逼,知其然后又感觉很简单一样,只是叫法不同而已,在移动性能要求日益严格的今天,在占面试一多半技能点的今天,我们还是要重视起来的。在Android专项性能分析中分析过各种工具的使用方法,这篇文章出自对微信性能分析一书的总结,大家有兴趣可以自己研读,在这里在简单提一下,我们既然知道这类问题了,那一定要掌握这类问题的衡量和定位标准,以及怎么发现这类问题(其实这类问题更多是经验发现,内存问题存在都不是一触即发的,都是日积月累的),大家经常用的有:

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

…(img-gbV2WKZu-1715747331939)]
[外链图片转存中…(img-l9IJyFHK-1715747331939)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值