Android 性能优化--内存优化分析总结

news2024/11/17 6:01:37

一、内存优化概念

1.1 为什么要做内存优化?

内存优化一直是一个很重要但却缺乏关注的点,内存作为程序运行最重要的资源之一,需要运行过程中做到合理的资源分配与回收,不合理的内存占用轻则使得用户应用程序运行卡顿、ANR、黑屏,重则导致用户应用程序发生 OOM(out of memory)崩溃。在你认真跟踪下来可能会发现内存出现问题的地方仅仅只是一个表现的地方,并非深层次的原因,因为内存问题相对比较复杂,它是一个逐渐挤压的过程,正好在你出现问题的代码那里爆了,所以针对应用的内存问题开发者必须多加关注。

1.2 内存问题表现形式

  • 内存抖动:锯齿状、GC频繁导致卡顿

  • 内存泄露:可用内存逐渐减少、频繁GC

  • 内存溢出:OOM、程序异常

二、常用内存分析工具

要解决内存问题,就必须要有强大的内存分析工具,让我们更快更方便的定位内存问题。目前主流的内存分析工具主要有 LeakCanary、Memory Profiler、MAT。

2.1 LeakCanary

LeakCanary是 Square 开源的一个内存泄露监控框架,在应用运行时出现的内存泄露会被 LeakCanary 监控记录。

 LeakCanary 内存泄漏的 trace 分析,主要看 Leaking:NO 到 Leaking:YES 这段的 trace,可以发现 TextView 出现了内存泄漏,因为它持有了被销毁的 Activity 的上下文 Context。

更具体的 trace 分析,具体可以查看官方文档 Fixing a memory leak。

使用 LeakCanary 虽然很方便,但是也有一定弊端:

  • 直接引用依赖使用的 LeakCanary 一般用于线下调试,应用发布到线上时需要关闭

  • 应用调试时有时候会引起卡顿

所以 一般使用 LeakCanary 只是一种简便定位内存泄露的方式,但如果需要更好的做内存优化,比如定位内存抖动、Bitmap 优化等还是需要其他的分析工具,主要常用的有 Memory Profiler 和 MAT。

2.2 NativeSize、Shallow Size、Retained Size、Depth

后续说明 Memory Profiler 和 MAT 时,会经常出现几个比较重要的指标:Shallow Size 和 Retained Size。在 Memory Profiler 还会提供 Native Size 和 Depth。

当您拿到一段 Heap Dump 之后,Memory Profiler 会展示出类的列表。对于每个类,Allocations 这一列显示的是它的实例数量。而在它右边则依次是 Native Size、Shallow Size 和 Retained Size:

我们用下图来表示某段 Heap Dump 记录的应用内存状态。注意红色的节点,在这个示例中,这个节点所代表的对象从我们的工程中引用了 Native 对象;这种情况不太常见,但在 Android 8.0 之后,使用 Bitmap 便可能产生此类情景,因为 Bitmap 会把像素信息存储在原生内存中来减少 JVM 的内存压力。

先从 Shallow Size 讲起,这列数据其实非常简单,就是 对象本身消耗的内存大小

Depth 是从 GC Root 到达这个实例的最短路径,图中的这些数字就是每个对象的深度 (Depth)。

一个对象离 GC Root 越近,它就越有可能与 GC Root 有多条路径相连,也就越可能在垃圾回收中被保存下来。

以红色节点为例,如果从其左边来的任何一个引用被破坏,红色节点就会变成不可访问的状态并且被垃圾回收回收掉。而对于右边的蓝色节点来说,如果您希望它被垃圾回收,那您需要把左右两边的路径都破坏才行。

值得警惕的是,如果您看到某个实例的 Depth 为 1 的话,这意味着它直接被 GC Root 引用,同时也意味着它永远不会被自动回收。

下面是一个示例 Activity,它实现了 LocationListener 接口,高亮部分代码 requestLocationUpdates() 将会使用当前 Activity 实例来注册 locationManager。如果您忘记注销,这个 Activity 就会泄漏。它将永远都待在内存里,因为位置管理器是一个 GC Root,而且永远都存在:

您能在 Memory Profiler 中查看这一情况。点击一个实例,Memory Profiler 将会打开一个面板来显示谁正在引用这个实例:

我们可以看到位置管理器中的 mListener 正在引用这个 Activity。您可以更进一步,通过引用面板导航至堆的引用视图,它可以让您验证这条引用链是否是您所预期的,也能帮您理解代码中是否有泄露以及哪里有泄露。

2.3 Memory Profiler

Memory Profiler 是内置在 Android Studio 适用于查看实时内存情况 的内存分析工具。

2.3.1 Memory Profiler 界面说明

官方文档:使用 Memory Profiler 查看 Java 堆和内存分配

2.3.2 Memory Profiler 查找内存抖动

查找内存抖动还是比较简单的,运行的程序在 Memory Profiler 会呈现为在短时间内内存上下波动频繁触发 GC 回收。

内存抖动比较常见的地方:

  • 自定义View的 onMeasure()、onLayout()、onDraw() 直接 new 创建对象

  • 列表比如 RecyclerView 的 onBindViewHolder() 直接 new 创建对象

  • 有循环的代码中创建对象

用一个简单的案例模拟内存抖动:

publicclassMainActivityextendsAppCompatActivity {
​@SuppressWarnings("HandlerLeak")privateHandler mHandler = newHandler() {@OverridepublicvoidhandleMessage(Message msg) {// 模拟内存抖动for (int i = 0; i < 100; i++) {String[] args = newString[100000];}
​mHandler.sendEmptyMessageDelayed(0, 30);}};
​@OverrideprotectedvoidonCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
​findViewById(R.id.button).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v) {mHandler.sendEmptyMessage(0);}});}
}
复制代码

案例非常简单,就是点击按钮时频繁的创建对象。在真机上运行上面的程序也许不会出现锯齿状的内存波动,但是会有非常频繁的 GC 回收,如下图:

那应该怎么具体的定位到是哪里发生的内存抖动呢?

按照上面的步骤操作:

  • 位置①:在程序处于运行时,点击 Record 按钮录制内存情况,然后点击 Stop 停止录制,会显示上图

  • 位置②:我们可以点击 Allocations 按降序从大到小或升序从小到大查看分配对象的数量,一般我们会选择降序从大到小看数量最多的对象。上图对象数量最多的是 String 对象

  • 位置③:在 Instance View 随便选择一个 String 对象,会显示下面的 Allocation Call Stack,它会显示这个对象的调用栈位置

  • 位置④: 从 Allocation Call Stack 可以看到,String 对象是在 MainActivity 的第 18 行 handleMessage() 创建的,从而也定位到内存抖动的位置

上面的操作还有一些小技巧:

  • 在位置①操作前,为了排除干扰,一般在录制前会先手动 GC 后再录制变化的内存;在 Android 8.0 以上的设备可以实时的拖动 Memory Profiler 选择查看的内存波动范围

  • 位置②上例是直接查看 Arrange by class,但实际项目中更多的会是选择 Arrange by package 查看自己项目包名下的类

2.3.3 Memory Profiler 查找内存泄露

上面讲到内存泄露的表现是会出现内存抖动,因为出现内存泄露时可用内存不断减少,系统需要内存时获取内存不足就会 GC,所以产生内存抖动。

出现内存泄露时 Memory Profiler 会呈现一个类似阶梯型的内存上升趋势,而且内存没有降下来:

上图的内存泄漏比较明显,实际项目开发中出现内存泄漏时可能不会特别明显,运行时间比较久才能发现内存是在缓慢上升的。这时候就需要 dump heap 帮助定位。

接下来会使用 Handler 内存泄露的案例简单讲解怎么使用 Memory Profiler 分析内存泄露。

public classHandlerLeakActivityextendsAppCompatActivity{private static finalStringTAG = HandlerLeakActivity.class.getSimpleName();
​privateHandler handler = newHandler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 0) {Log.i(TAG, "handler receive msg");}}};
​@Overrideprotected void onCreate(@NullableBundle savedInstanceState) {super.onCreate(savedInstanceState);handler.sendEmptyMessageDelayed(0, 10 * 1000);}
}
复制代码

上面代码非常简单,就是启动 app 后,每次进入 HandlerLeakActivity 就使用 Handler 延迟 10s 发送消息,在 10s 内退出界面,不断重复操作。

1、重复多次可能内存泄露的操作,Memory Profiler 堆转储出 hprof 文件(建议在操作前先 GC 排除干扰)

2、在 Memory Profiler 查看查看堆转储文件 hprof:

可以发现经过手动 GC 后,Allocations 显示有 5 个 HandlerLeakActivity,堆转储 Instance View 下也仍显示有多个 Activity 实例,说明已经内存泄露,具体的内存泄露定位可以在 Instance View 泄露的实例类对象中点击查看,Instance View 下面的 Reference 会显示具体的引用链。

在新版本的 Memory Profiler 提供了 Activity/Fragment Leaks 复选框,选中它可以直接找到可能内存泄露的位置:

2.4 MAT

2.4.1 MAT简介

  • 强大的Java Heap分析工具,查找内存泄漏及内存占用

  • 生成整体报告、分析问题等

  • 线下深入使用

官网下载地址:www.eclipse.org/mat/downloa… ,这个地址是不是有你熟悉的单词,嗯,没错啦,MAT是Eclipse中的一个插件,因为现在开发过程中很多人都使用了IDEA或者Android Studio,所以你不想下载Eclipse的话呢,你可以去下载MAT的独立版,解压之后里面有一个MemoryAnalyzer.exe的可执行文件,直接点击就可以使用了。

这个工具很多时候我们需要结合Android Studio的堆转储能力配合使用,但是需要注意,AS3.0之后生成的hprof文件不是标准的hprof文件了,需要使用命令转换一下:hprof-conv 原文件路径 转换后文件路径

2.4.2 MAT用法简介

①、Overview:概览信息

Top Consumers

  • 通过图表展示出占用内存比较多的对象,此栏在做降低内存占用时比较有帮助

  • Biggest Objects:相对详细的信息

Leak Suspects

  • 快速查看内存泄露的可疑点

②、 Histogram:直方图

  • Class Name:具体检索某一个类

  • Objects:某一个具体的Class有多少实例

  • Shallow Heap:某单一实例自己占了多少内存

  • Retained Heap:在这个引用链之上这些对象总共占了多少内存

Group by packge:将类对象以包名形式展示

List objects

  • with outgoing references:自身引用了哪些类

  • with incoming references:自身被哪些类引用

③、dominator_tree

  • 每个对象的支配树

  • percentage:占所有对象的百分比

在条目上右键它也有List objects,它和Histogram之间有啥区别呢?主要区别就是下面两点:

  • Histogram:基于类的角度分析

  • dominator_tree:基于实例的角度分析

④、OQL:对象查询语言,类似于从数据库中检索内容

⑤、thread_overview:详细的展示线程信息,可以查看出当前内存中存在多少线程

三、实战内存抖动解决

3.1 内存抖动简介

  • 定义:内存频繁分配和回收导致内存不稳定

  • 表现:频繁GC、内存曲线呈锯齿状

  • 危害:导致卡顿、严重时会导致OOM

3.2 内存抖动导致OOM

  • 频繁创建对象,导致内存不足及碎片(不连续)

  • 不连续的内存片无法被分配,导致OOM

3.3 实战分析

这一部分我会模拟一次内存抖动,并通过Profiler分析内存情况,定位到具体内存抖动的代码。

首先先来创建一个布局文件activity_memory.xml,里面就一个按钮,用来触发模拟内存抖动的那部分代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_memory"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="模拟内存抖动"/>
</LinearLayout>
复制代码

然后定义一个MemoryShakeActivity页面,加载刚才的布局,并且在页面中定义一个Handler,当点击模拟内存抖动的按钮时,我们定时执行handleMessage中的模拟抖动的代码,整个代码都是很容易能看懂的那种:

/*** 说明:模拟内存抖动页面*/publicclassMemoryShakeActivityextendsAppCompatActivityimplementsView.OnClickListener {@SuppressLint("HandlerLeak")privatestaticHandler mHandler = newHandler(){@OverridepublicvoidhandleMessage(@NonNull Message msg) {super.handleMessage(msg);//模拟内存抖动的场景,每隔10毫秒执行一次,循环执行100次,每次通过new分配大内存for (int i=0;i<100;i++){String[] obj = newString[100000];}mHandler.sendEmptyMessageDelayed(0,10);}};@OverrideprotectedvoidonCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory);findViewById(R.id.btn_memory).setOnClickListener(this);}@OverridepublicvoidonClick(View view) {if (view.getId() == R.id.btn_memory){mHandler.sendEmptyMessage(0);}}@OverrideprotectedvoidonDestroy() {super.onDestroy();mHandler.removeCallbacksAndMessages(null);}
}
复制代码

然后跑起来,我截了两张图给大家看一下,第一张是没有执行模拟抖动的代码之前的,第二张是执行之后的:

从上面两张图中可以清晰的看到第一张内存比较平稳,第二张内存图有锯齿状出现,突然出现了频繁的GC,看到下面好多小垃圾桶了没,这个时候可以初步判定应该是出现了内存抖动现象,因为比较符合它的特征,然后在面板上拖动一段距离它就会将这段时间内的内存分配情况给我们展示出来:

首先双击Allocations,然后将这一列按照从大到小的顺序排列好,然后你会发现String数组居然有这么多,它占用的内存大小也是最高的(值得关注的点我都用矩形标出了),此时我们就应该锁定这个目标,为什么String类型的数组会有这么多,这里很有可能是有问题的。然后排查究竟是哪里导致的这个问题,很简单点击String[]这一行,在右侧Instance View面板中随便点击一行,下方Allocation Call Stack面板中就出现了对应的堆栈信息,上面也列出了具体哪个类的哪一行,右键jupm to source就可以跳转到指定的源码位置,这样就找到了内存抖动出现的位置,然后我们分析代码作相应的修改即可。

流程总结:

  1. 使用Memory Profiler初步排查;

  1. 使用Memory Profiler或CPU Profiler结合代码排查

内存抖动解决技巧:找循环或者频繁调用的地方

四、实战内存泄露解决

4.1 内存泄露简介

定义:内存中存在已经没有用的对象

表现:内存抖动、可用内存逐渐变少

危害:内存不足、GC频繁、OOM

4.2 实战分析

这里还是通过代码来真实的模拟一次内存泄露的场景,对于一般的APP程序来说,最大的问题往往都是在Bitmap上,因为它消耗的内存比较多,拿它来模拟会看的比较明显。好首先来看布局文件activity_memoryleak.xml,里面就一个ImageView控件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_memoryleak"android:layout_width="50dp"android:layout_height="50dp" />
</LinearLayout>
复制代码

然后定义了一个模拟处理某些业务的Callback回调接口,和一个统一管理这些回调接口的Manager类:

//模拟回调处理某些业务场景publicinterfaceCallBack {voiddpOperate();
}//统一管理CallbackpublicclassCallBackManager {publicstatic ArrayList<CallBack> sCallBacks = new ArrayList<>();publicstaticvoidaddCallBack(CallBack callBack) {sCallBacks.add(callBack);}publicstaticvoidremoveCallBack(CallBack callBack) {sCallBacks.remove(callBack);}
}
复制代码

然后在我们的模拟内存泄露的页面上设置Bitmap,并设置回调监听:

/*** 说明:模拟内存泄露页面*/
public classMemoryLeakActivityextendsAppCompatActivityimplementsCallBack{@Overrideprotected void onCreate(@NullableBundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memoryleak);ImageView imageView = findViewById(R.id.iv_memoryleak);Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.big_bg);imageView.setImageBitmap(bitmap);CallBackManager.addCallBack(this);}@Overridepublic void dpOperate() {}
}
复制代码

OK,我们的代码就写完了,现在来实际运行一下,然后将这个页面连续打开关闭多次,看看这段代码会不会造成内存泄露呢?

这是我用Profiler截取的内存图片,可以看到整个内存在经过了我的反复开关页面之后虽然有的地方出现了一个小抖动,但是整体是呈阶梯状上升的,可用内存在逐渐减少,此时基本上可以断定这个界面出现了内存泄露。Profiler工具虽然可以初步帮我们断定出现了内存泄露,但是它却无法断定具体是哪里出现了内存泄露,意思就是我们还是不知道该修改哪里的代码,所以此时需要用到强大的Java Heap工具了,来有请MAT出场。

首先需要在Profiler中点击Dump Java Heap按钮,使用堆转储功能转换成一个文件,然后点击保存按钮将文件保存到本地目录下,比如我这里保存为H盘中的memoryleak.hprof文件,然后使用hprof-conv命令将其转换为标准的hprof文件,我这里是转换后的名称是:memoryleak_transed.hprof,如下所示:

然后打开MAT工具,导入刚刚生成的转换后的文件:

点击Histogram查看内存中所有存活的对象,然后我们在Class Name中可以输入内容搜索想要查找的对象:

然后可以看到该对象的具体信息,以及数量和所占用的内存大小,我这里发现内存中居然存在6个MemoryLeakActivity对象:

然后右键List objects---->with incoming references找到所有引向它的强引用:

然后右键Path To GC Roots----->with all references,将所有引用都计算在内然后算出来这个对象和GCRoot之间的路径:

来看结果,最后是到了sCallBacks这里,而且它左下角有个小圆圈,这就是我们真正要找的位置,也就是说MemoryLeakActivity是被CallBackManager这个类的sCallBacks这个对象引用了:

根据上面找的结果到代码中去找CallBackManager的sCallBacks看看这里究竟是做了什么引发的?

publicstaticArrayList<CallBack> sCallBacks = new ArrayList<>();
复制代码

MemoryLeakActivity是被sCallBacks这个静态变量引用着,由于被static关键字修饰的变量的生命周期是和App的整个生命周期一样长的,所以当MemoryLeakActivity这个页面关闭时,我们应该将变量的引用关系给释放掉,否则就出现了上面的内存泄露的问题。所以解决这个问题也很简单了,添加如下几行代码:

@OverrideprotectedvoidonDestroy() {super.onDestroy();CallBackManager.removeCallBack(this);
}
复制代码

流程总结:

  1. 使用Memory Profiler初步观察(可用内存逐渐减少);

  1. 通过Memory Analyzer结合代码确认

五、线上内存监控方案

线上内存问题最大的就是内存泄露,对于内存抖动和内存溢出它们一般都和内存泄露导致的内存无法释放相关,如果能够解决内存泄露,则线上内存问题就会减少很多。线上内存监控其实还是比较困难的,因为我们无法使用线下的这些工具来直观的发现分析问题。

5.1 常规方案

①、设定场景线上Dump

比如你的App已经占用到单个App最大可用内存的较高百分比,比如80%,通过:Debug.dumpHprofData();这行代码可以实现将当前内存信息转化为本地文件。

整个流程如下:超过内存80%——>内存Dump——>回传文件(注意文件可能很大,保持在wifi状态回传)——>MAT手动分析

总结:

  • Dump文件太大,和对象数正相关,可裁剪

  • 上传失败率高、分析困难

②、LeakCanary线上使用

  • LeakCanary带到线上

  • 预设泄露怀疑点

  • 发现泄露回传

总结:

  • 不适合所有情况,必须预设怀疑点,限制了全面性

  • 分析比较耗时,也容易OOM(实践发现LeakCanary分析过程较慢,很有可能自己在分析的过程中自身发生OOM)

5.2 LeakCanary定制

  • 预设怀疑点——》自动找怀疑点(谁的内存占用大就怀疑谁,大内存对象出现问题的概率更大)

  • 分析泄露链路慢(分析预设对象的每一个对象)——》分析Retain Size大的对象(减少它的分析工作量,提高分析速度)

  • 分析OOM(将内存堆栈生成的所有文件全部映射到内存中,比较占用内存)——》对象裁剪,不全部加载到内存

5.3 线上监控完整方案

  • 待机内存、重点模块内存、OOM率

  • 整体及重点模块GC次数、GC时间

  • 增强的LeakCanary自动化内存泄露分析

六、内存优化技巧

优化大方向:

  • 内存泄露

  • 内存抖动

  • Bitmap

优化细节:

  • LargeHeap属性(虽然有点耍流氓,但还是应该向系统申请)

  • onTrimMemory、onLowMemory(系统给的低内存的回调,可以根据不同的回调等级去处理一些逻辑)

  • 使用优化过的集合:SparseArray

  • 谨慎使用SharedPreference(一次性load到内存中)

  • 谨慎使用外部库(尽量选择经过大规模验证的外部库)

  • 业务架构设计合理(加载的数据是你能用到的,不浪费内存加载无用数据)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/984988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

maven(总)

maven maven的简介 maven主要服务于基于java平台的项目构建&#xff0c;依赖管理和项目信息管理 主要体现在项目和管理 瀑布式开发&#xff1a;在做项目的时候要求有明确的需求&#xff0c;必须按照需求一步一步去做好规划&#xff0c;在项目的运行过程中严格的产出一些文档 …

gdal求矢量图形的形心

gdal求矢量图形的形心 #include "gdal_priv.h" #include "ogrsf_frmts.h"int main() {OGRRegisterAll();OGRPolygon* square_1 new OGRPolygon();OGRLinearRing* ring_1 new OGRLinearRing();// 添加 square_1 的点ring_1->addPoint(0, 0);ring_1-&g…

js--15----闭包是什么?说说闭包的使用场景

1、闭包是什么&#xff1f; 一个函数和对其周围状态&#xff08;词法环境&#xff09;的引用绑定在一起&#xff08;或者说函数被引用包围&#xff09;&#xff0c;这样的组合就是是闭包&#xff08;closure&#xff09;&#xff0c;也就是说&#xff0c;闭包让你在一个内层函数…

win环境安装SuperMap iserver和配置许可

SuperMap iServer是我国北京超图公司研发的基于跨平台GIS内核的云GIS应用服务器产品&#xff0c;通过服务的方式&#xff0c;面向网络客户端提供与专业GIS桌面产品相同功能的GIS服务&#xff0c;能够管理、发布多源服务&#xff0c;包括REST服务、OGC服务等。 SuperMap iserve…

23062C++QT day2

封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个公有成员函数&#xff1a;void…

AI 时代的向量数据库、关系型数据库与 Serverless 技术丨TiDB Hackathon 2023 随想

TiDB Hackathon 2023 刚刚结束&#xff0c;我仔细地审阅了所有的项目。 在并未强调项目必须使用人工智能&#xff08;AI&#xff09;相关技术的情况下&#xff0c;引人注目的项目几乎一致地都使用了 AI 来构建自己的应用。 大规模语言模型&#xff08;LLM&#xff09;的问世使得…

监听Helm release资源

监听Helm release资源 基于helm做部署管理工具时&#xff0c;可能想要管理用户已有环境&#xff0c;这时需要将已有环境中的release信息上报到业务系统中。当用户在环境中部署新的release时&#xff0c;也需要实时监听并上报回来。下面将讲解如何去监听release资源 helm rele…

天津web前端培训班 前端是否适合零基础学?

随着HTML 5和ECMAScript 6的正式发布&#xff0c;大量的前端业务逻辑&#xff0c;极大地增加了前端的代码量&#xff0c;前端代码的模块化、按需加载和依赖管理势在必行&#xff0c;因此Web前端越来越被人们重视。 Web前端的就业前景 Web前端开发工程师薪资持续走高&#xff…

LoadRunner参数化详解

安装打开loadrunner时&#xff0c;发现虽然自己的思想还在&#xff0c;但已经非常生疏了&#xff0c;好多设置都找不到了具体的位置。下面说参数化参数化是性能测试中时最常用的一种技巧吧&#xff01;这里需要说明的是&#xff0c;不是只有loadrunner才可以设置参数化&#xf…

《热题100》字符串、双指针、贪心算法篇

思路&#xff1a;对于输入的的字符串&#xff0c;只有三种可能&#xff0c;ipv4,ipv6,和neither ipv4:四位&#xff0c;十进制&#xff0c;无前导0&#xff0c;小于256 ipv6:八位&#xff0c;十六进制&#xff0c;无多余0&#xff08;00情况不允许&#xff09;&#xff0c;不…

JAVA设计模式第十讲:SPI - 业务差异解决方案

JAVA设计模式第十讲&#xff1a;SPI - 业务差异解决方案 我们需要在不修改源代码的情况下&#xff0c;动态为程序提供一系列额外的特性。首先想到的是Spring的AOP技术来构建应用插件&#xff0c;但是在Java自带的插件中&#xff0c;就有完整的实现。SPI&#xff08;Service Pro…

OJ练习第165题——修车的最少时间

修车的最少时间 力扣链接&#xff1a;2594. 修车的最少时间 题目描述 给你一个整数数组 ranks &#xff0c;表示一些机械工的 能力值 。ranksi 是第 i 位机械工的能力值。能力值为 r 的机械工可以在 r * n2 分钟内修好 n 辆车。 同时给你一个整数 cars &#xff0c;表示总…

pyspark 系统找不到指定的路径; \Java\jdk1.8.0_172\bin\java

使用用具PyCharm 2023.2.1 1&#xff1a;pyspark 系统找不到指定的路径&#xff0c; Java not found and JAVA_HOME environment variable is not set. Install Java and set JAVA_HOME to point to the Java installation directory. 解决方法&#xff1a;配置正确环境变量…

博物馆网上展厅有哪些用途,如何搭建数字时代的文化宝库

引言&#xff1a; 博物馆一直以来都是保存、展示和传承人类文化遗产的地方。然而&#xff0c;随着数字时代的来临&#xff0c;博物馆也逐渐迎来了创新的变革。博物馆网上展厅&#xff0c;作为一种新型的文化传播方式&#xff0c;正逐渐崭露头角。 一.什么是博物馆网上展厅&…

JAVA 比较两个区间是否存在交集

最近遇到一个开发问题&#xff0c;判断两个价格的大小&#xff0c;听着很简单&#xff0c;但其实价格是浮动的&#xff0c;也就是说价格是一个范围&#xff0c;比如物品A的价格是5&#xff5e;10&#xff0c;现在我们通过筛选条件&#xff0c;把价格符合在8&#xff5e;20之前的…

解锁无限的检索能力:谷歌的MEMORY-VQ以卓越的压缩力彻底改变了LLM

检索增强是一种常用且有效的方法&#xff0c;用于增强语言模型的事实知识&#xff0c;同时加快模型推理时间。尽管如此&#xff0c;这种方法带来了相当大的计算成本&#xff0c;这归因于存储预先计算的表示所需的大量存储需求。 为了解决这一相关问题&#xff0c;谷歌研究小组…

【漏洞复现】时空智友企业流程化管控系统文件上传

漏洞描述 通过时空智友该系统,可让企业实现流程的自动化、协同上提升、数据得洞察及决策得优化,来提高工作效率、管理水平及企业的竞争力。时空智友企业流程化 formservice接口处存有任意文件上传漏洞,未经认证得攻击者可利用此接口上传后门程序,可导致服务器失陷。 免责…

webrtc的FULL ICE和Lite ICE

1、ICE的模式 分为FULL ICE和Lite ICE&#xff1a; FULL ICE:是双方都要进行连通性检查&#xff0c;完成的走一遍流程。 Lite ICE: 在FULL ICE和Lite ICE互通时&#xff0c;只需要FULL ICE一方进行连通性检查&#xff0c; Lite一方只需回应response消息。这种模式对于部署在公网…

Qt应用开发(基础篇)——工具按钮类 QToolButton

一、前言 QToolButton类继承于QAbstractButton&#xff0c;该部件为命令或选项提供了一个快速访问按钮&#xff0c;通常用于QToolBar中。 按钮基类 QAbstractButton QToolButton是一个特殊的按钮&#xff0c;一般显示文本&#xff0c;只显示图标&#xff0c;结合toolBar使用。它…

《protobuf》入门

protobuf 初始protobuf简单上手编写protobuf编译 .proto 文件编写测试文件 testPB.cc 初始protobuf Protocol Buffers 是 Google 的一种语言无关、平台无关、可扩展的序列化结构数据的 方法&#xff0c;它可用于&#xff08;数据&#xff09; 通信协议、数据存储等。 Protocol …