在 ArkTS 中,如何有效地进行内存管理和避免内存泄漏?

ArkTS 是鸿蒙生态的应用开发语言,它在 TypeScript 的基础上进行了优化和定制,以适应鸿蒙系统的需求。

以下是在 ArkTS 中进行有效的内存管理和避免内存泄漏:

1. 使用 constlet 合理声明变量:

  • 使用 const 声明那些不会重新赋值的变量,这有助于确保变量的不变性,并可能让编译器进行更多的优化。
  • 使用 let 声明那些需要重新赋值的变量,避免使用 var,因为 var 会导致变量提升到函数作用域的顶部,可能会引起意外的错误。

在 ArkTS 中,constlet 是用来声明变量的关键字,它们在作用域和可变性方面有所不同。以下是使用 constlet 合理声明变量的示例代码对比:

使用 const 声明不变的变量:

// 正确的使用方式:使用 const 声明一个不会被重新赋值的变量
const PI = 3.14159; // PI 是一个常量,不应该被重新赋值// 尝试重新赋值将会导致编译错误
// PI = 3.14; // Error: Cannot assign to 'PI' because it is a read-only property.

使用 let 声明可变的变量:

// 正确的使用方式:使用 let 声明一个可能会被重新赋值的变量
let count = 0; // count 是一个变量,可以被重新赋值// 可以重新赋值
count = 1;
console.log(count); // 输出:1

对比示例:

function vgFunction() {// 使用 const 声明一个常量,表示这个变量不应该被修改const name = "VG";console.log(name); // 输出:VG// 使用 let 声明一个变量,表示这个变量可能会被修改let age = 18;console.log(age); // 输出:18,永远18// 根据某些条件修改变量if (age < 30) {age = 30;}console.log(age); // 输出:30
}vgFunction();

在这个例子中,name 被声明为常量,表示它的值不应该改变,而 age 被声明为变量,表示它的值可能会改变。使用 constlet 可以清晰地表达出变量的预期用途,有助于代码的可读性和维护性。

避免使用 var 的示例:

// 不推荐使用 var,因为它会提升变量到函数作用域的顶部
function exampleFunction() {var globalVar = "I name is VG"; // 这实际上是一个全局变量console.log(globalVar); // 输出:I name is VG
}exampleFunction();
console.log(globalVar); // 输出:I name is VG

在这个例子中,使用 var 声明的 globalVar 实际上是一个全局变量,即使它在函数内部声明。这可能会导致意外的副作用,因为全局变量可以在程序的任何地方被访问和修改。因此,推荐使用 constlet 来替代 var

2. 避免全局变量:

  • 尽量减少全局变量的使用,因为全局变量在整个应用生命周期中都存在,难以管理,容易造成内存泄漏。

全局变量是指在全局作用域中声明的变量,它们可以在整个程序的任何地方被访问和修改。过度使用全局变量可能会导致代码难以维护、理解和调试,因为它们可以在任何地方被改变,增加了代码的耦合性。以下是避免全局变量的示例代码对比:

使用全局变量的示例:

// 全局变量
var globalCounter = 0;function increment() {globalCounter++; // 直接修改全局变量
}function decrement() {globalCounter--; // 直接修改全局变量
}increment();
console.log(globalCounter); // 输出:1
decrement();
console.log(globalCounter); // 输出:0

在这个例子中,globalCounter 是一个全局变量,可以在 incrementdecrement 函数中被直接访问和修改。这可能会导致在程序的其他部分不小心修改了这个变量,从而产生难以追踪的错误。

避免使用全局变量的示例:

// 避免使用全局变量,改为使用局部变量和参数传递
function counterManager() {let counter = 0; // 局部变量function increment() {counter++; // 修改局部变量}function decrement() {counter--; // 修改局部变量}return {increment: increment,decrement: decrement,getCount: function () {return counter;}};
}const counter = counterManager(); // 创建一个局部变量 counter 来持有管理器对象
counter.increment();
console.log(counter.getCount()); // 输出:1
counter.decrement();
console.log(counter.getCount()); // 输出:0

在这个例子中,我们创建了一个 counterManager 函数,它返回一个对象,包含 incrementdecrementgetCount 方法。这些方法操作的是 counterManager 函数内部的局部变量 counter,而不是全局变量。这样,counter 的值就被封装在 counterManager 函数的作用域内,不会影响到全局作用域中的其他变量。

通过这种方式,我们减少了全局变量的使用,提高了代码的封装性和模块化,使得代码更易于维护和理解。同时,这也有助于避免全局变量可能引起的命名冲突和意外的副作用。

3. 使用弱引用(Weak References):

  • 对于不需要长期持有的对象,可以使用弱引用,这样垃圾回收器可以更容易地回收这些对象。

在 ArkTS 或 TypeScript 中,并没有内置的弱引用(Weak References)概念,这是因为 JavaScript 引擎(包括 V8,即 Node.js 和大多数浏览器的 JavaScript 引擎)默认就是使用垃圾回收来管理内存的。弱引用通常是指那些不阻止垃圾回收器回收其所引用对象的引用。

然而,我们可以通过一些设计模式来模拟弱引用的行为,尤其是在处理大型对象或者循环引用时。如何避免循环引用导致的内存泄漏,来看这个例子:

可能导致内存泄漏的循环引用示例:

class Person {name: string;friends: Person[]; // 朋友列表constructor(name: string) {this.name = name;this.friends = [];}addFriend(friend: Person) {this.friends.push(friend);// 这里创建了一个循环引用,friend.friends.push(this) 会使得 person 和 friend 互相引用friend.friends.push(this);}
}const personA = new Person("VG");
const personB = new Person("Vin");
personA.addFriend(personB);
// 此时,personA 和 personB 互相引用,形成了循环引用

在这个例子中,Person 类的每个实例都维护了一个朋友列表,当一个 Person 实例被添加到另一个 Person 实例的朋友列表时,同时也将后者添加到前者的朋友列表中,形成了循环引用。

避免循环引用的示例:

为了避免循环引用,我们可以不直接在 Person 类中添加彼此的引用,而是通过其他方式来管理这种关系,比如使用一个外部的映射或者服务来管理朋友关系。

class Person {name: string;friends: string[]; // 朋友列表,只存储名字而不是直接引用对象constructor(name: string) {this.name = name;this.friends = [];}addFriend(name: string) {this.friends.push(name);// 这里不再创建循环引用,而是将朋友的名字添加到列表中}
}const personA = new Person("VG");
const personB = new Person("Vin");
personA.addFriend(personB.name);
// 此时,personA 的 friends 列表中只有 personB 的名字,不会造成循环引用

在这个改进的例子中,我们不再直接将 Person 对象添加到朋友列表中,而是只存储朋友的名字。这样,即使 Person 对象之间有多个连接,也不会形成循环引用,从而避免了潜在的内存泄漏问题。

如何通过设计模式来避免循环引用,这是在 JavaScript 和 TypeScript 中管理内存和避免内存泄漏的一种常用方法。在某些特定的 JavaScript 环境中,如 Node.js,可以使用弱引用(WeakRef)和弱映射(WeakMap)这样的内置对象来更直接地实现弱引用。但在大多数前端 JavaScript 环境中,这些对象并不可用。

4. 及时清理不再使用的对象:

  • 当对象不再需要时,应该手动将其设置为 null 或删除其引用,这样垃圾回收器可以回收这部分内存。

在 JavaScript 或 TypeScript 中,及时清理不再使用的对象是避免内存泄漏的重要策略。这通常涉及到移除事件监听器、取消网络请求、销毁定时器等操作。以下是一个业务场景的示例代码对比,展示如何及时清理不再使用的对象:

未及时清理不再使用的对象示例:

// 假设我们有一个组件,它在加载时订阅了一个事件
class Component {private eventListener: () => void;constructor() {this.eventListener = () => {console.log('Event triggered');};document.addEventListener('customEvent', this.eventListener);}// 组件被销毁时,应该清理事件监听器destroy() {// 忘记移除事件监听器// document.removeEventListener('customEvent', this.eventListener);}
}const myComponent = new Component();
// 当组件不再需要时,应该调用 destroy 方法
// myComponent.destroy();

在这个例子中,Component 类在构造时添加了一个事件监听器,但在 destroy 方法中忘记移除这个监听器。如果 myComponent 被销毁而没有调用 destroy 方法,事件监听器仍然存在,这将导致内存泄漏,因为 myComponent 和它的 eventListener 方法仍然被事件监听器引用。

及时清理不再使用的对象示例:

class Component {private eventListener: () => void;constructor() {this.eventListener = () => {console.log('Event triggered');};document.addEventListener('customEvent', this.eventListener);}// 组件被销毁时,及时清理事件监听器destroy() {document.removeEventListener('customEvent', this.eventListener);}
}const myComponent = new Component();
// 当组件不再需要时,调用 destroy 方法
myComponent.destroy();

在这个改进的例子中,Component 类在 destroy 方法中正确地移除了事件监听器。这样,当组件不再需要时,通过调用 destroy 方法,可以确保不会有任何遗留的引用,从而避免内存泄漏。

使用定时器时及时清理示例:

class TimerComponent {private timerId: number;constructor() {this.timerId = window.setInterval(() => {console.log('Timer tick');// 定时器执行的代码}, 1000);}// 组件被销毁时,清除定时器destroy() {clearInterval(this.timerId);}
}const myTimerComponent = new TimerComponent();
// 当定时器组件不再需要时,调用 destroy 方法
// myTimerComponent.destroy();

在这个例子中,TimerComponent 类使用 setInterval 创建了一个定时器。在 destroy 方法中,使用 clearInterval 清除了定时器,这样可以避免定时器继续执行并引用 TimerComponent 实例,从而避免内存泄漏。

我们可以看到及时清理不再使用的对象对于防止内存泄漏是多么重要。在实际开发中,我们应该养成良好的习惯,确保在对象不再需要时,清理所有相关的资源。

5. 使用事件监听时注意移除监听器:

  • 在添加事件监听器时,确保在不需要监听时移除它们,否则即使对象本身不再被使用,事件监听器也会保持对象的引用,导致内存泄漏。

在 JavaScript 或 TypeScript 中,使用事件监听是常见的交互方式,但如果没有在适当的时候移除监听器,可能会导致内存泄漏。如何正确地添加和移除事件监听器,上代码:

未移除事件监听器的示例:

class Widget {private element: HTMLElement;constructor(selector: string) {this.element = document.querySelector(selector)!;this.element.addEventListener('click', this.handleClick);}// 处理点击事件的方法handleClick = () => {console.log('Widget clicked');}// 假设有一个方法来销毁 Widget 实例,但没有移除事件监听器destroy() {// 应该在这里移除事件监听器,但被遗漏了// this.element.removeEventListener('click', this.handleClick);}
}const widget = new Widget('#myWidget');
// 当 widget 不再需要时,应该调用 destroy 方法
// widget.destroy();

在这个例子中,Widget 类在构造函数中为元素添加了一个点击事件监听器。然而,在 destroy 方法中,我们忘记了移除这个监听器。如果 widget 实例被销毁而没有调用 destroy 方法,事件监听器仍然存在,这将导致 Widget 实例和它的 handleClick 方法被持续引用,从而造成内存泄漏。

正确移除事件监听器的示例:

class Widget {private element: HTMLElement;constructor(selector: string) {this.element = document.querySelector(selector)!;this.element.addEventListener('click', this.handleClick);}// 处理点击事件的方法handleClick = () => {console.log('Widget clicked');}// 销毁 Widget 实例,并移除事件监听器destroy() {this.element.removeEventListener('click', this.handleClick);}
}const widget = new Widget('#myWidget');
// 当 widget 不再需要时,调用 destroy 方法
widget.destroy();

在这个改进的例子中,Widget 类在 destroy 方法中正确地移除了点击事件监听器。这样,当 widget 实例不再需要时,通过调用 destroy 方法,可以确保不会有任何遗留的引用,从而避免内存泄漏。

使用事件委托的示例:

class WidgetManager {private container: HTMLElement;constructor(selector: string) {this.container = document.querySelector(selector)!;this.container.addEventListener('click', this.handleClick);}// 使用事件委托来处理子元素的点击事件handleClick = (event: MouseEvent) => {const target = event.target as HTMLElement;if (target.classList.contains('widget')) {console.log('Widget clicked');}}// 销毁 WidgetManager 实例,并移除事件监听器destroy() {this.container.removeEventListener('click', this.handleClick);}
}const widgetManager = new WidgetManager('#widgetsContainer');
// 当 widgetManager 不再需要时,调用 destroy 方法
widgetManager.destroy();

在这个例子中,WidgetManager 类使用事件委托来处理所有子元素的点击事件。这样做的好处是,即使子元素是后来动态添加的,我们也不需要为它们单独添加事件监听器。当 widgetManager 实例不再需要时,通过调用 destroy 方法,可以移除事件监听器,避免内存泄漏。

我们可以看到在适当的时候移除事件监听器对于防止内存泄漏是多么重要。在实际开发中,我们应该确保在组件或对象销毁时,清理所有相关的事件监听器。

6. 合理使用闭包:

  • 闭包可以持续访问函数外部的变量,如果不当使用,可能会导致内存泄漏。确保在不需要闭包时释放相关资源。

闭包是一个强大的特性,它允许一个函数访问其定义时的作用域链。然而,不当使用闭包可能会导致内存泄漏,因为闭包会保持对外部作用域的引用,从而阻止垃圾回收。

不当使用闭包的示例:

// 假设我们有一个函数,用于创建计数器
function createCounter() {let count = 0;return function() {console.log(++count);};
}const counter = createCounter();
counter(); // 输出:1
counter(); // 输出:2// 假设我们不再需要这个计数器,但是由于闭包,count 变量仍然被引用
// 这可能会导致内存泄漏,如果 createCounter 被频繁调用

在这个例子中,每次调用 createCounter 都会创建一个新的闭包,它捕获了 count 变量。如果 createCounter 被频繁调用,每个闭包都会保持对 count 的引用,即使 counter 函数不再被使用。

合理使用闭包的示例:

// 改进后的计数器函数,使用一个外部对象来存储计数
const counter = (function() {let count = 0;return {increment: function() {console.log(++count);},value: function() {return count;}};
})();counter.increment(); // 输出:1
counter.increment(); // 输出:2// 当计数器不再需要时,可以将其设置为 null,帮助垃圾回收器回收内存
counter = null;

在这个改进的例子中,我们使用了一个立即执行的函数表达式(IIFE)来创建一个包含 count 变量的对象。这样,所有的计数器都共享同一个 count 变量,而不是每个闭包都有自己的副本。当计数器不再需要时,我们可以将 counter 设置为 null,这有助于垃圾回收器回收内存。

使用闭包进行数据绑定的示例:

// 一个简单的数据绑定函数
function bindData(element, data) {const template = document.createElement('div');template.innerHTML = `<strong>${data.name}</strong>: ${data.value}`;element.appendChild(template);// 使用闭包来更新数据return function update(newData) {template.innerHTML = `<strong>${newData.name}</strong>: ${newData.value}`;};
}const dataWidget = bindData(document.body, { name: 'Initial', value: 'Data' });
dataWidget({ name: 'Updated', value: 'Data' });
// 当数据绑定不再需要时,可以将其设置为 null
dataWidget = null;

在这个例子中,bindData 函数创建了一个闭包,用于更新绑定到 DOM 元素的数据。当数据更新时,我们调用返回的 update 函数。当数据绑定不再需要时,我们可以将 dataWidget 设置为 null,这有助于垃圾回收器回收内存。

我们应该确保在不需要闭包时释放相关资源,以避免不必要的内存占用。

7. 利用垃圾回收机制:

  • 理解 ArkTS 的垃圾回收机制,合理组织代码结构,以便于垃圾回收器高效工作。

在 ArkTS 中,利用垃圾回收机制同样重要,因为它可以帮助开发者管理内存,避免内存泄漏:

不利用垃圾回收机制的示例:

@Entry
@Component
struct MyComponent {private resource: any;build() {// 假设这里加载了一个资源,但没有提供释放资源的方法this.resource = this.loadResource('resource.json');}private loadResource(url: string): any {// 资源加载逻辑return new ResourceData();}// 组件销毁时,没有释放资源onDestroy() {// 应该在这里释放资源,但被遗漏了}
}

在这个例子中,MyComponent 组件在 build 方法中加载了一个资源,但没有提供释放资源的方法。在组件销毁时,也没有释放资源,这可能会导致内存泄漏。

利用垃圾回收机制的示例:

@Entry
@Component
struct MyComponent {private resource: any;build() {// 假设这里加载了一个资源,并提供了释放资源的方法this.resource = this.loadResource('resource.json');}private loadResource(url: string): any {// 资源加载逻辑return new ResourceData();}private releaseResource() {// 释放资源逻辑this.resource = null;}// 组件销毁时,释放资源onDestroy() {this.releaseResource();}
}

在这个改进的例子中,MyComponent 组件在 build 方法中加载了一个资源,并在 releaseResource 方法中提供了释放资源的逻辑。在组件销毁时,调用 releaseResource 方法来释放资源,这样可以帮助垃圾回收器回收资源占用的内存。

利用垃圾回收机制的另一个示例:

@Entry
@Component
struct MyComponent {private timerId: number;build() {// 设置一个定时器,用于定期执行某些操作this.timerId = setInterval(() => {this.performTask();}, 1000);}private performTask() {// 执行某些任务console.log('Task performed');}// 组件销毁时,清除定时器onDestroy() {clearInterval(this.timerId);}
}

在这个例子中,MyComponent 组件在 build 方法中设置了一个定时器。在组件销毁时,调用 clearInterval 来清除定时器,这样可以避免定时器继续执行并引用组件,从而避免内存泄漏。

我们可以看到在 ArkTS 中如何通过编写良好的代码习惯来配合垃圾回收机制,确保及时释放不再需要的资源。

8. 避免不必要的对象创建:

  • 在循环或频繁调用的函数中,避免创建不必要的新对象,这会增加垃圾回收的负担。

在 ArkTS 中,避免不必要的对象创建是优化性能和内存使用的一个重要方面。如何在 ArkTS 中避免不必要的对象创建呢,来看一下代码示例:

不必要的对象创建示例:

@Entry
@Component
struct MyComponent {build() {for (let i = 0; i < 1000; i++) {// 在循环中创建了1000个不必要的新对象const data = this.createDataObject(i);console.log(data);}}private createDataObject(index: number): DataObject {// 假设 DataObject 是一个复杂的对象return new DataObject(index);}
}class DataObject {constructor(public index: number) {// 构造函数中可能包含一些初始化逻辑}
}

在这个例子中,MyComponent 组件的 build 方法在循环中创建了1000个 DataObject 实例。如果这些对象在循环之后不再需要,这种创建和立即丢弃的做法会导致不必要的内存分配和潜在的性能问题。

避免不必要的对象创建示例:

@Entry
@Component
struct MyComponent {private dataObjects: DataObject[] = [];build() {for (let i = 0; i < 1000; i++) {// 复用已有的对象,而不是在每次迭代中创建新对象if (!this.dataObjects[i]) {this.dataObjects[i] = this.createDataObject(i);} else {this.dataObjects[i].update(i); // 假设 DataObject 有一个更新方法}console.log(this.dataObjects[i]);}}private createDataObject(index: number): DataObject {return new DataObject(index);}
}class DataObject {constructor(public index: number) {}update(newIndex: number) {this.index = newIndex;}
}

在这个改进的例子中,MyComponent 组件维护了一个 DataObject 数组。在循环中,它首先检查是否已经存在对象,如果不存在,则创建新对象;如果已存在,则调用 update 方法更新对象的数据。这种方式避免了在每次迭代中创建新对象,从而减少了内存分配和提高了性能。

使用对象池模式避免不必要的对象创建示例:

@Entry
@Component
struct MyComponent {private objectPool: DataObject[] = new Array(1000).fill(null).map(() => new DataObject());build() {for (let i = 0; i < 1000; i++) {// 从对象池中获取对象,而不是每次都创建新对象const data = this.objectPool[i];data.update(i);console.log(data);}}
}class DataObject {constructor(public index: number) {}update(newIndex: number) {this.index = newIndex;}
}

在这个例子中,MyComponent 组件使用了一个对象池来管理 DataObject 实例。对象池在组件初始化时一次性创建了足够数量的对象,并在循环中复用这些对象。这种方法可以显著减少对象创建和销毁的开销,特别是在对象生命周期短且频繁创建销毁的场景中。

开发中,我们要考虑对象的生命周期和使用场景,尽可能地复用对象,或者使用对象池模式来管理对象的创建和销毁。

9. 使用对象池模式:

  • 对于频繁创建和销毁的对象,可以考虑使用对象池模式来重用对象,减少内存分配和回收的开销。

对象池模式是一种常用的优化技术,特别是在处理大量短生命周期对象时,它可以帮助减少内存分配和垃圾回收的开销。我以游戏场景为例,来讲一下如何使用对象池模式:

未使用对象池模式的示例:

@Entry
@Component
struct GameComponent {private poolSize: number = 10;build() {for (let i = 0; i < this.poolSize; i++) {this.spawnEnemy();}}private spawnEnemy() {// 创建一个新的敌人对象const enemy = new Enemy();// 将敌人添加到游戏世界this.addEnemyToGameWorld(enemy);}private addEnemyToGameWorld(enemy: Enemy) {// 添加到游戏世界的逻辑console.log('Enemy added to game world:', enemy);}
}class Enemy {constructor() {// 敌人对象的初始化逻辑console.log('Enemy created');}
}

在这个例子中,GameComponent 组件在 build 方法中循环创建了10个 Enemy 对象。每次调用 spawnEnemy 方法都会创建一个新的 Enemy 实例,这在创建大量敌人时可能会导致性能问题。

使用对象池模式的示例:

@Entry
@Component
struct GameComponent {private enemyPool: Enemy[] = [];private poolSize: number = 10;onCreate() {// 初始化敌人对象池for (let i = 0; i < this.poolSize; i++) {this.enemyPool.push(new Enemy());}}build() {for (let i = 0; i < this.poolSize; i++) {this.spawnEnemy();}}private spawnEnemy() {// 从对象池中获取一个敌人对象const enemy = this.enemyPool.shift(); // 移除并获取数组的第一个元素if (enemy) {// 将敌人添加到游戏世界this.addEnemyToGameWorld(enemy);} else {// 如果对象池为空,则创建一个新的敌人对象const newEnemy = new Enemy();this.addEnemyToGameWorld(newEnemy);this.enemyPool.push(newEnemy); // 将新创建的敌人对象放回池中}}private addEnemyToGameWorld(enemy: Enemy) {// 添加到游戏世界的逻辑console.log('Enemy added to game world:', enemy);}
}class Enemy {constructor() {// 敌人对象的初始化逻辑console.log('Enemy created');}reset() {// 重置敌人对象的状态,以便再次使用console.log('Enemy reset for reuse');}
}

在这个改进的例子中,GameComponent 组件使用了一个 enemyPool 数组作为对象池来管理 Enemy 对象。在 onCreate 方法中,我们预先创建了一定数量的 Enemy 对象并放入池中。当需要创建新的敌人时,我们首先尝试从对象池中获取一个现有的对象。如果对象池为空,我们才创建一个新的敌人对象,并在添加到游戏世界后将其放回池中。此外,我们添加了一个 reset 方法来重置敌人对象的状态,以便它可以被重复使用。

使用对象池模式可以显著减少在游戏或动画中创建和销毁对象的次数,从而提高性能和减少内存压力。在 ArkTS 中,这种模式尤其适用于那些频繁创建和销毁对象的场景,如粒子系统、游戏中的敌人、子弹等。

最后

有效地管理 ArkTS 应用中的内存使用,减少内存泄漏的风险,并提高应用的性能和稳定性,这在 ArkTS编码中同样至关重要,你在使用 ArkTS的过程中,还有其它有效管理内存的经验吗,欢迎评论区告诉我,国产替代,支持鸿蒙,我们都是一份子。

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

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

相关文章

USB总线开关量DIO采集卡,24路数字量输入输出及32位计数器卡USB5801

阿尔泰科技 型号&#xff1a;USB5801 概述&#xff1a; 产品应用&#xff1a; 指标参数&#xff1a; 数字量 通道数 24路&#xff0c;每8路可配置成输入或输出 电气标准 TTL兼容 数字量输入 高电平的低电压&#xff1a;2V 低电平的高电压&#xff1a;0.8V 数字量输出 …

数据结构基础讲解(七)——数组和广义表专项练习

本文数据结构讲解参考书目&#xff1a; 通过网盘分享的文件&#xff1a;数据结构 C语言版.pdf 链接: https://pan.baidu.com/s/159y_QTbXqpMhNCNP_Fls9g?pwdze8e 提取码: ze8e 数据结构基础讲解&#xff08;六&#xff09;——串的专项练习-CSDN博客 个人主页&#xff1a;樱娆…

替代区块链

随着比特币的成功&#xff0c;人们逐渐意识到区块链技术的潜力&#xff0c;并随之出现了迅速的发展&#xff0c;各种区块链协议、应用程序和平台相应产生。 需要指出的是&#xff0c;在这种多元的局面下&#xff0c;很多项目迅速失去了它们的吸引力。事实上&#xff0c;有不少项…

ITK-高斯滤波

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 高斯滤波原理 高斯滤波&#xff08;Gaussian Blur&#xff09;是数字图像处理中常见的一种平滑滤波器&#xff0c;旨在通过模糊处…

捉虫笔记(四)-- 空格引发的悬案

空格引发的悬案 1、描述现象: 在代码中有一段利用rmdir指令删除目录代码&#xff0c;但是有用户反馈一直删除失败&#xff0c;但是有没有看到错误的日志信息&#xff0c;正好有同事能复现,所以今天好好探究一番。 2、思考过程 很好奇的一点就是为什么有的环境就是正常。 首…

Edge-Triggered模式:反应堆

Linux: Linux Code - Gitee.comhttps://gitee.com/RuofengMao/linux/tree/master/Reactor

yolov5-6.2 在 rk3399pro 上的移植

文章目录 一、搭建yolov5环境二、导出onnx模型三、安装 rknn-toolkit四、通过netron 查看 yolov5s.onnx 输出节点五、创建 onnx2rknn.py 文件六、通过toolkit将onnx转换为rknn模型七、在rk3399pro开发板上运行rknn模型 一、搭建yolov5环境 从yolov5官方仓库 ultralytics/yolov…

[论文笔记] LLM大模型剪枝篇——2、剪枝总体方案

https://github.com/sramshetty/ShortGPT/tree/main My剪枝方案(暂定): 剪枝目标:1.5B —> 100~600M 剪枝方法: 层粒度剪枝 1、基于BI分数选择P%的冗余层,P=60~80 2、对前N%冗余层,直接删除full layer。N=20(N:剪枝崩溃临界点,LLaMA2在45%,Mistral-7B在35%,Qw…

【DS18B20 简单开发】

DS18B20 是一种数字温度传感器&#xff0c;由 Maxim Integrated 生产。它提供了一个非常简单的方式来将温度测量集成到微控制器系统中。以下是关于 DS18B20 的一些关键特性&#xff1a; 单总线协议&#xff1a;DS18B20 使用单总线&#xff08;1-Wire&#xff09;数字通信协议&…

HTML添加文字

一、创建HTML5文档基本标签 <!DOCTYPE html> //定义文档类型 <html> //定义HTML文档<head> //定义关于文档的信息<title>文档标题</title> //定义文档的标题<meta charset"utf-8" /> //定义文档的字符编码</head&…

远程连接Hiveserver2服务

目录 1.修改 core-site.xml 和 hive-site.xml 的配置文件 2.启动HiveServer2服务 3.启动Beeline工具连接Hiveserver2服务 4.利用IDEA工具连接Hiveserver2服务 完成Hive本地模式安装后&#xff0c;可以启动hiveserver2服务进行远程连接和操作Hive。 1.修改 core-site.xml …

rancher upgrade 【rancher 升级】

文章目录 1. 背景2. 下载3. 安装4. 检查5. 测试5.1 创建项目5.2 创建应用5.3 删除集群5.4 注册集群 1. 背景 rancher v2.8.2 升级 v2.9.1 2. 下载 下载charts helm repo add rancher-latest https://releases.rancher.com/server-charts/latest helm repo update helm fetc…

常见概念 -- 光回波损耗

什么是回波损耗 回波损耗&#xff0c;又称为反射损耗&#xff0c;当高速信号进入或退出光纤的某个部分&#xff08;例如光纤连接器&#xff09;&#xff0c;不连续和阻抗不匹配会引起反射&#xff0c;这就是光纤回波损耗。器件的回波损耗Return Loss(RL)是光信号的输入端口的反…

C++ | Leetcode C++题解之第395题至少有K个重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution { public:int longestSubstring(string s, int k) {int ret 0;int n s.length();for (int t 1; t < 26; t) {int l 0, r 0;vector<int> cnt(26, 0);int tot 0;int less 0;while (r < n) {cnt[s[r] - a];…

MATLAB下载详细教程及下载链接

欢迎大家进评论区交流经验 1. 准备工作 下载MATLAB安装包&#xff1a;首先&#xff0c;从MathWorks官方网站&#xff08;http://www.mathworks.com&#xff09;下载适合您操作系统的MATLAB安装包。确保选择与您的操作系统&#xff08;如Windows、macOS或Linux&#xff09;兼容的…

西门子PLC与HMI之间的时间同步工控小周

HMI 时间同步功能工控人加入PLC工业自动化精英社群 HMI 设备具有时间同步功能&#xff0c;利用 HMI 设备的该功能&#xff0c;可实现 PLC 和 HMI 之间的时间同步&#xff0c;进而实现多个 PLC 之间的时间同步。 HMI 设备时间同步的属性&#xff1a; 1.HMI 设备既可作为主站对…

Reflection 70B——HyperWrite推出的大型语言模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Vue Echarts报错Initialize failed: invalid dom解决方法

此问题是图表初始化时 找不到dom&#xff0c;以下是解决方法 1、不要用created&#xff08;用mounted&#xff09;&#xff0c;created这时候还只是创建了实例&#xff0c;但模板还没挂载完成&#xff1b; created&#xff1a; 在模板渲染成 html 前调用&#xff0c;通常初始…

通信工程学习:什么是ASK振幅键控、FSK频移键控、PSK相移键控

ASK振幅键控、FSK频移键控、PSK相移键控 ASK&#xff08;振幅键控&#xff09;、FSK&#xff08;频移键控&#xff09;和PSK&#xff08;相移键控&#xff09;是三种常见的数字调制技术&#xff0c;它们各自通过不同的方式改变载波的某个参数来传输数字信息。以下是对这三种调制…

大模型实战一、Ollama+RagFlow 部署本地知识库

大模型实战一、OllamaRagFlow 部署本地知识库 参考你提供的文章&#xff0c;这里是基于 Windows 系统通过 Docker 安装部署 RagFlow 和 Ollama 的本地化大模型知识库的详细教程。本文将指导你如何在 Windows 上使用 Docker 来设置 RagFlow 和 Ollama 环境&#xff0c;并安装通…