一、团队效率提升
- 版本更新
Android增量更新与热修复
增量更新的目的是为了减少更新app所需要下载的包体积大小,常见如手机端游戏,apk包体积为几百M,但有时更新只需下载十几M的安装包即可完成更新。
增量更新demo地址:https://github.com/po1arbear/bsdiff-android
热修复一般是用于当已经发布的app有Bug需要修复的时候,开发者修改代码并发布补丁,让应用能够在不需要重新安装的情况下实现更新,主流方案有Tinker、AndFix等。
管理软件更新
1.自然升级 2.引导升级
进度感知(用户可以看见更新进度),提醒链路(导航栏打开更新弹窗)。
提醒更新策略原则 不阻塞行为操作
2023年工信部发布了《关于进一步提升移动互联网应用服务能力》的通知,其中第四条更是明确表示“窗口关闭用户可选”,所以强更弹窗并不是我们的最佳选择。
主要字段
标题
内容
最新版本号
取消倒计时时长
是否提醒
是否强更
双端最低支持的系统版本
最大提醒次数
未更新最大版本间隔等等
版本治理


- 编译优化
- 编译流程(需要了解)
- 全量编译/增量编译
- 有效方案:
- 升级硬件
- Moudle arr化
- 构建缓存
- 其他方案(作用不大)
1.使用最新 版本工具2.Debug环境只运行编译需要的资源3.版本将图片转为WebP4.格式停用PNG5.开启gradle缓存6.开启kotlin增量和并行编译7.使用静态依赖项版本8.合理调整堆大小9.kapt优化10.使用增量注解处理器
- 检测编译耗时
Build Analyaer模块查看
Gradle命令
$ gradlew app:assemble --profile (Windows)
$ ./gradlew app:assembleDebug --profile (Ubuntu,Mac)
scan命令,扫描后在网址中打开查看性能(推荐使用)
$ gradlew app:assemble --scan(Windows)
$ ./gradlew app:assembleDebug --scan(Ubuntu,Mac)
Android studio 配置修改:https://juejin.cn/post/7094198918065422350
Moudle arr开源方案参考:https://juejin.cn/post/7038157787976695815,这个插件可以直接引用到项目中。
只编译文件变更位置,两种实现方式,通过git上传记录或者本地window变更文件找到需要重新编译的位置。
- 内存优化
1.内存概念
虚拟内存(用户空间 内核空间)
虚拟内存是计算机系统内有管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分限成多个物理内存碎片,还有部分暂时存储在外部总盘存健客上,在需要时进行数据交换。
堆
Java堆:堆实际上是一块匿名共享内存,Android虚拟机仅仅只是把它封装成一个mSpace。由底层C库管理,仍然可以使用libc提供的函数malloc和free分配和释放内存。
虚拟机:早期Dalvik,现在ART。不同点:lmage Space存放一些预加载类,在Zygote进程和应用程序进程之间共享Large Object Space离散地址的集合,分配一些大对象,用于提高GC的管理效率和整体性能
Native堆
2.内存优化
目的
1.降低OOM,抖动,泄漏 提升稳定性
2.减少内存占用 接高应用后台运行时的存活率
3.减少卡顿 提高流畅性
背景
Java GC回收:1.判斯对象是否回收的可达性分析算法2.强软弱虚4种引用类型3.GC回收算法
内存泄漏:内存泄漏指的是一块内存没有被使用且无法被GC回收
内存抖动:内存抖动意味着短时间频繁创建对象与回收,容易触发GC而当GC时所有线程都会停止,因此可能导致卡顿
内存溢出:申请的内存超出可用的内存,即OOM
- Java堆内存超限1堆内存单次分配过大2多次分配累计过大3内存累计分配触顶4无足够连续内存空间
- 文件描述符(fd)数目超限
- pthread create 1线程数超限 2虚拟空间不足
内存优化工具
Android Profiler、MAT推荐、VisualVM
内存监控工具
线下LeakCanary、线上Matrix、Koom
内存优化常规
- 合适的代码设计,数据结构
- 对象复用,SimplePool与SynchronizedPool
- 资源,复用,序列化Parcelable
- 避免内存泄漏,抖动,溢出... 图片
图片优化
- BitMap内存演变
- 图片优化
统一图片库使用glide coil 低端机使用Fresco
设备分极优化策略 比如低端机单进程
- 大图监控 Glide--SingleRequest--onResourceReady、Epic
- 重复图片检测
重复图片检测的原理:使用内存Hprof分析工具,自动将重复Bitmap的图片和引用堆找输出。
- 建立全局的线上Btmap监控 ASM编译插桩-Gradle Transform
内存泄漏
已动态分配的堆内存由于未知原因未释放或无法释放
- 永久性泄漏 泄漏的内存永远不会回收
- 临时性泄漏 泄漏的内存未来某些时间可能被回收
泄漏场景
- 资源未关闭 finish关闭
- 监听器 成对出现
- 系统Bug InputMethodManager mCurRootView/AccountManager使用不当导致的泄漏等
- 集合泄漏 主动clear null
- webview泄漏 ondestory
- 第三方库泄漏
- 线程
Thread 不持有
HandlerThread
1.onDestroy方法中调用HandlerThread的quit方法2.将HandlerThread定义为静态内部类3.使用ApplicationContext
- Handler
1 onDestroy执行时,调用Handler的removeCallbacksAndMessage,Runnable(Thread)泄漏则可通过终止线程
2 将Handler,Runnable(Thread)定义为静态内部类
3 使用弱引用,或者使用ApplicationContext
4 耗时操作jobschedule
- Static
Activity
找到static变量泄漏的Activity的时机将其引用链剪断
将static变量回复为非static变量,使其可以正常GC
如果确实需要context.需要保持static.则可使用Application Context
- View
通过设计改变static为普通变量 不要在Android中时用static修饰View
onDestroy时将static view 置为null
- 内部类持有外部类引用
非静态内部类 静态内部类 匿名内部类 尽量不要使用
内存溢出
00M 原因
- 进程虚拟内存不足
- Java堆内存不足 一般是内存泄缩或者内存使用不合理导致
- 连续内存不足 相对较少
- FD数量超过系统限制
- 线程数量超过系统限刚
- 手机物理内存不足
Native内存和内存监控
内存指标监控
二、Java基础
-
锁
1.1 并发和并行、同步和异步
- 并发:同一时间段,多个任务交替执行,时间片轮法共享系统资源(Synchronized)
- 并行:单位时间内,多个任务在多个独立处理单元上同时执行(分布式锁)
- 同步:任务依次执行,任务需要依次执行(阻塞)
- 异步:可以同时进行多个任务,任务的调用和完成是独立的(非阻塞)
1.2 并发编程三大特性
可见性:是指一个线程对共享变量进行修改,另一个线程立即得到修改后的最新值。
原子性:在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行。
有序性:解决了重排序。重排序:Java在编译时和运行时会对代码进行优化,会导致程序最终的执行顺序不一定就是我们编写代码时的顺序。
锁搞定了多线程中的重排序,单线程中怎么解决重排序?使用as-if-serial--语句
1.3 锁类型
-
- 线程要不要锁住同步资源? a.锁住 悲观锁 b.不锁住 乐观锁
悲观锁:悲观锁的策略是在访问数据之前先获取锁,这表示它悲观地认为在整个数据操作过程中都会有并发修改的风险。因此,在悲观锁的机制下,当一个线程访问数据时,会先加锁,其他线程要想访问该数据就必须等待已持有锁的线程释放锁才能继续操作。典型的悲观锁实现包括数据库中的行级锁、Java中的synchronized关键字等。
乐观锁:相对于悲观锁,乐观锁则采取一种更加乐观的态度,它假设在大多数情况下数据不会发生冲突。因此,乐观锁在更新数据时并不加锁,而是先读取数据版本信息,然后在写回数据时检查数据版本信息是否发生变化。典型的乐观锁实现方式包括版本号机制和CAS(Compare And Swap)等。
悲观锁适合写操作频繁的场景,适合于长事务,而乐观锁适合读操作频繁的场景,适合于短事务。在实际应用中,选择合适的锁策略需要根据具体的业务场景和性能需求进行权衡。
-
- 锁住同步资源失败,线程要不要阻塞? a.阻塞 b.不阻塞 自旋锁 适应性自旋锁
自旋锁是一种基于忙等待的锁机制,它在获取锁时不会立即放弃CPU的执行时间片,而是通过循环进行空转等待其他线程释放锁。这样可以减少线程切换的开销,适用于短时间内竞争锁的情况。
自旋锁的基本思想是,当一个线程发现该锁已被其他线程占用时,它会不断地在循环中尝试获取锁,直到获取到为止。如果在循环中一直无法获取到锁,该线程可能会消耗较多的CPU资源。
为了提高自旋锁的效率,有时候可以采用适应性自旋锁(Adaptive Spin Lock)。适应性自旋锁是根据之前获取锁的历史记录来调整自旋等待的时间。如果一个线程在过去经常能够快速获取到锁,那么它的自旋等待时间会较短;相反,如果一个线程在过去很少能够快速获取到锁,那么它的自旋等待时间会较长。
适应性自旋锁可以根据线程的历史获取情况来动态调整自旋等待时间,更有效地利用CPU资源。例如,如果一个线程经常能够快速获取到锁,那么它的自旋时间可以较短,减少无谓的空转等待时间;如果一个线程经常无法快速获取到锁,那么它的自旋时间可以适当延长,减少不必要的锁竞争。
适应性自旋锁的具体实现方式可以根据不同的编程语言和平台而有所不同。在Java中,JVM会使用一些统计信息来调整自旋等待时间,以提高自旋锁的性能。
-
- 多个线程竞争同步资源的流程细节有没有区别?
- 不锁住资源,多个线程中只有一个能修改资源成功,其它线程会重试 无锁
- 同一个线程执行同步资源时自动获取资源 偏向锁
- 多个线程竞争同步资源时,没有获取资源的线程自旋等待锁释放 轻量级锁
- 多个线程竞争同步资源时,没有获取资源的线程阻塞等待唤醒 重量级锁
偏向锁:偏向锁是为了解决大多数情况下都是单线程访问同步块的情况。当一个线程首次访问同步块时,偏向锁会将对象头部的标记设置为指向该线程,并且记录下该线程的ID。此后,如果同步块再次被相同的线程访问,无需进行同步操作,可以直接进入临界区执行,从而减少了获取锁和释放锁的开销。
轻量级锁:轻量级锁是针对多线程竞争同步块的情况进行优化的,它通过CAS操作来尝试获取锁。当只有一个线程在竞争同步块时,轻量级锁使用CAS操作将对象头部的标记替换为指向锁记录的指针,从而避免了传统的互斥量操作。
重量级锁:重量级锁是指当多个线程竞争同步块时,锁会膨胀为重量级锁,在这种情况下会引入互斥量,使得其他线程阻塞,等待持有锁的线程释放锁。
这三种锁在Java中的实现是为了在不同的多线程场景下提供最佳的性能和资源利用率。偏向锁适用于大多数情况下都是单线程访问同步块的场景,轻量级锁适用于少量线程竞争同步块的场景,而重量级锁适用于多个线程竞争同步块的场景。 Java虚拟机在运行时会根据实际情况动态地选择合适的锁机制来提高程序的性能。
-
- 多个线程竞争锁时要不要排队?
a.排队 公平锁 b.先尝试插队,插队失败再排队 非公平锁
在并发编程中,公平锁和非公平锁是两种不同的锁获取策略。它们影响了线程在竞争锁时的获取顺序和公平性。
公平锁:公平锁是指当多个线程按照申请锁的顺序来获取锁,即先来先得的原则。当一个线程发出获取锁的请求时,如果当前锁被其他线程占用,该线程会进入等待队列,按照先后顺序等待锁的释放。当锁释放时,等待时间最长的线程会获得锁。公平锁的特点是能够保证锁的获取是按照线程的申请顺序进行的,避免了线程饥饿的情况。
非公平锁:非公平锁是指当多个线程竞争锁时,不考虑申请锁的顺序,允许"插队",即如果一个线程发出获取锁的请求时,如果当前锁没有被其他线程占用,那么该线程可以直接获取到锁,而无需进入等待队列。这样可能导致某些线程会一直获取到锁,而其他线程长时间得不到执行,出现线程饥饿的情况。
在实际应用中,选择公平锁或非公平锁取决于具体的业务场景和性能需求。公平锁能够保证线程按照申请顺序获取锁,但可能会带来一定的性能开销;而非公平锁可能会提高系统的吞吐量,但可能造成某些线程长时间得不到执行的情况。因此,在使用锁的时候需要根据实际情况进行选择,以达到最佳的性能和公平性。
-
- 一个线程中的多个流程能不能获取同一把锁? 能可重入锁 不

1992

被折叠的 条评论
为什么被折叠?



