如何解决写入放大效应导致的内存问题,最新HarmonyOS鸿蒙高级面试题汇

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

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

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

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

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

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

这段代码会将整个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专项性能分析中分析过各种工具的使用方法,这篇文章出自对微信性能分析一书的总结,大家有兴趣可以自己研读,在这里在简单提一下,我们既然知道这类问题了,那一定要掌握这类问题的衡量和定位标准,以及怎么发现这类问题(其实这类问题更多是经验发现,内存问题存在都不是一触即发的,都是日积月累的),大家经常用的有:

写入放大问题最直观的体现是应用程序在运行过程中占用的内存过高,可能导致系统内存不足,从而影响应用程序的性能和稳定性。一般来说,我们可以通过以下几种方式来衡量和定位这个问题:

  1. 使用内存分析工具,如Android Profiler、MAT等,来监控应用程序的内存使用情况,并查看内存使用情况是否存在异常。
  2. 在应用程序运行过程中,使用Log输出应用程序的内存使用情况,如可用内存、已用内存等,以便在后期进行分析。
  3. 使用dumpsys命令来获取应用程序的内存使用情况,如:

adb shell dumpsys meminfo <package_name>

这条命令可以输出应用程序当前的内存使用情况,包括堆内存、Native堆内存、Dalvik堆内存等信息。
4. 使用GC日志来分析内存使用情况,GC日志可以记录应用程序中的内存分配、回收等信息,通过分析GC日志可以发现内存使用情况是否存在异常。

作者:狼窝山下的青年
链接:https://juejin.cn/post/7202164243612532792

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

点击文末CSDN官方认证微信卡片免费领取↓↓↓**

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-JqFtutyn-1713395621992)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值