关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。
目录
- 一、导读
- 二、概览
- 三、案例分析
-
- [3.1 使用memory-profiler](#3.1 使用memory-profiler)
- [3.2 使用 cpu-profiler](#3.2 使用 cpu-profiler)
- [四、 推荐阅读](#四、 推荐阅读)
一、导读
我们继续总结学习Java基础知识,温故知新。
二、概览
内存抖动是指内存不稳定,频繁分配和回收,导致内存不稳定,其表现形式为频繁GC,
内存抖动可能会导致以下问题:
- 性能下降:由于频繁的内存分配和回收操作,系统的性能会受到影响,导致运行速度变慢。
- 程序崩溃、OOM:内存抖动可能导致内存分配错误或者内存泄漏,导致程序崩溃或者运行不稳定。
- 系统资源消耗:内存抖动会增加系统资源的消耗,包括内存和CPU的占用率,可能导致系统负载增加。
内存抖动大部分都是由于频繁创建对象 导致,会占用大量内存,同时会产生大量的内存碎片,不连续的内存碎片很多情况下是无法被分配的。
从而导致OOM的产生。
要防止内存抖动,可以采取以下几个方法和策略:
-
合理规划内存分配:在设计和编写代码时,要合理规划内存的分配和释放,避免频繁的内存分配和回收操作。可以使用对象池、缓冲区等技术来预先分配和管理一块内存,减少内存分配的开销。
-
避免内存碎片:内存碎片是指内存中存在一些被分割成小块的未被使用的空间。内存碎片会导致内存分配失败或效率低下。可以通过使用内存池、内存复用等方法来减少内存碎片的产生。另外,考虑使用内存管理工具或垃圾回收机制来自动进行内存碎片整理和回收。
-
优化算法和数据结构:一些算法和数据结构可能会导致内存抖动,例如频繁的动态数组扩容和收缩操作。可以使用更合适的数据结构或算法,降低内存抖动的概率或频率。比如使用链表代替数组,使用平衡二叉树代替线性查找等。
-
设置适当的内存分配策略:根据实际情况,可以根据内存使用情况和需求,设置合适的内存分配策略。可以使用内存池、内存缓存等技术来预先分配和管理内存,减少频繁的内存分配与回收操作。
-
进行内存性能优化:对于大型或长时间运行的应用程序,可以进行内存性能优化。可以使用内存分析工具和性能分析工具来检测内存使用情况,找出内存抖动的原因,并针对性地进行优化。
-
进行定期的内存测试和性能评估:定期进行内存测试和性能评估,可以发现潜在的内存抖动问题,并进行及时修复和优化。
三、案例分析
不同的工具有不同的使用场景,对应线下场景,我们先用 android studio自带的工具
3.1 使用memory-profiler
可以直观的展示内存使用情况,我们先上一段代码,来模拟内存申请及释放
java
public class MainActivity extends AppCompatActivity {
// Used to load the 'example' library on application startup.
static {
System.loadLibrary("example");
}
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 循环1000次
for (int index = 0; index <= 1000; index++){
// 然后弄一个相对耗内存的操作,制造内存抖动
String args[] = new String[1000000];
}
mHandler.sendEmptyMessageDelayed(0,50);
}
};
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendEmptyMessageDelayed(0,2000);
}
});
}
/**
* A native method that is implemented by the 'example' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
代码运行后,我们进行内存的保存,快速查看Java 和 Kotlin 分配情况分析
通过上面的图,我们可以看到在频繁的gc,
那我们怎么才能知道内存抖动真正发生在哪里呢,看上图,string数组非常多,我们点一下这个string数组,如下图:
上面就出现了一个Call Stack 标签页,显示该实例被分配到何处以及在哪个线程中,我们可以明显的看到 handlemessage,
然后右键选择jump to source,之间跳转到源码查看。
Allocations : 此类创建的实例对象数量
Total count :对象在堆中未被回收的数量
3.2 使用 cpu-profiler
使用方式跟上面差不错,我们保存文件,然后查看,
跟踪这一段CPU执行的时间,
如果发现某一段(应用自有函数的调用)代码(即绿色的条形段)在反复地被执行,便是内存抖动的地方