Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
深入探讨JavaScript中的内存管理_创想鸟

深入探讨JavaScript中的内存管理

深入探讨JavaScript中的内存管理

内存管理是编程语言的基本能力,JavaScript 中的内存管理是通过 V8 完成的。V8 的实现遵循 ECMA-262 规范,而规范中没有阐述内存布局以及内存管理相关信息,所以它的原理取决于解释器的实现。唯一肯定的是不管任何编程语言,内存的生命周期是一致的:

分配所需要的内存;使用分配的内存(读、写);不需要时将其释放、归还。

基于此背景下本文试图通过内存的生命周期拓展 JavaScript 的内存布局。【推荐学习:javascript视频教程】

开始分配内存之前需要先了解一下数据类型与数据结构。

数据类型

JavaScript 数据类型分为 基本类型 与 引用类型。

立即学习“Java免费学习笔记(深入)”;

1.png

基本类型:在语言最低层且不可变的值称为原始值。所有原始值都可以使用 typeof 运算符测试所属基本类型(除了null,因为typeof null === “object”)。所有原始值都有它们相应的对象包装类(除了 null 和 undefined),这为原始值提供可用的方法。基本类型的对象包装类有 Boolean、Number、String、Symbol。

引用类型:表示内存中的可变的值,JavaScript 中对象是唯一可变的。Object、Array、函数等都属于对象。给对象定义属性可通过 Object.defineProperty() 方法,读取对象属性信息可通过 Object.getOwnPropertyDescriptor()。

基本类型与引用类型可以互转,转换的行为称为 装箱 与 拆箱。

装箱:基本类型 => 引用类型 e.g: new String(‘call_me’)

拆箱:引用类型 => 基本类型 e.g: new String(’64’).valueOf()、new String(’64’).toString()

以下是一些开发过程中常见的类型转换:

number -> string: let a = 1 => a+”” / String(a)string -> number: let a = “1” => +a / ~~a / Number(a)any -> boolean: let a = {} => !a / !!a / Boolean(a)

从内存角度区分基本类型与应用类型,关键在于值在内存中是否可变,基本类型更新会重新开辟空间并改变指针地址,引用类型更新不会改变指针地址但会改变指针所指向的对象;从代码上看,引用类型由基本类型和 {} 组成。

数据结构

2.png

JavaScript 程序运行时 V8 会给程序分配内存,这种内存称为 Resident Set(常驻内存集合),V8 常驻内存进一步细分成 Stack 和 Heap。

Stack(栈) 是自动分配大小固定的内存空间,并由系统自动释放。栈数据结构遵循先进后出的原则,线性有序存储,容量小,系统分配效率高。

Heap(堆) 是动态分配大小不固定的内存空间,不会自动释放(释放依赖 GC)。堆数据结构是一棵二叉树结构,容量大,速度慢。

一个线程只有一个栈内存空间,一个进程只有一个堆空间。

栈内存空间默认大小是 864KB,也可通过 node --v8-options | grep -B0 -A1 stack-size 查看。

栈结构其实经常可以看到,当写了一段报错代码时,控制台的错误提示就是一个栈结构。从下往上看调用路径,最顶部就是错误位置。例如最顶部抛出 Maxium call stack size exceeded 错误就代表当前调用超出了栈的限制。

堆中的结构划分为 新空间(New Space)、旧空间(Old Space)、大型对象空间(Large object space)、代码空间(Code-space)、单元空间(Cell Space)、属性单元空间(Property Cell Space) 和 映射空间(Map Space),新空间和旧空间在后面会详细介绍。

大型对象空间(Large object space):大于其他空间大小限制的对象存放在这里。每个对象都有自己的内存区域,这里的对象不会被垃圾回收器移动。

代码空间(Code-space):存储已编译的代码块,是唯一可执行的内存空间。

单元空间(Cell Space)、属性单元空间(Property Cell Space)和映射空间(Map Space):这些空间分别存放 Cell,PropertyCell 和 Map。这些空间包含的对象大小相同,并且对对象类型有些限制,可以简化回收工作。

每个空间(除了大型对象空间(Large object space))都由若干个 Page 组成。一个 page 是由操作系统分配的一个连续内存块,一个内存块大小为 1MB。

从内存角度区分栈与堆,关键在于用完是否立即释放。

相信读者们看到这里肯定会联想到数据类型与堆栈的关联,网上和一些书籍的结论是:原始值分配在栈上,而对象分配在堆上。这个说法真的对吗?带着问题我们进入第二步:使用分配的内存。

内存模型

Node 提供了 process.memoryUsage() 方法描述 Node.js 进程的内存使用情况(以字节 Bytes 为单位)

$ node> process.memoryUsage()

假设原始值分配在栈上,而对象分配在堆上是对的,结合栈空间只有 864KB。如果我们声明一个 10MB 的字符串,看看堆内存是否会发生变化。

const beforeMemeryUsed = process.memoryUsage().heapUsed / 1024 / 1024;const bigString = 'x'.repeat(10*1024*1024) // 10 MBconsole.log(bigString); // need to use the string otherwise the compiler would just optimize it into nothingnessconst afterMemeryUsed = process.memoryUsage().heapUsed / 1024 / 1024;console.log(`Before memory used: ${beforeMemeryUsed} MB`); // Before memory used: 3.7668304443359375 MBconsole.log(`After memory used: ${afterMemeryUsed} MB`); // After memory used: 13.8348388671875 MB

堆内存消耗接近 10 MB,说明字符串存储在堆中。

那么小字符串以及其他基本类型是否同样的存储在堆中呢,我们借助谷歌浏览器的 Memery 堆快照(Heap snapshot)进行分析。

打开谷歌浏览器无痕模式 Console 中输入以下代码,并分析执行前后的变量变化。

function testHeap() {    const smi = 18;    const heapNumber = 18.18;    const nextHeapNumber = 18.18;    const boolean = true;    const muNull = null;    const myUndefined = undefined;    const symbol = Symbol("my-symbol");    const emptyString = "";    const string = "my-string";    const nextString = "my-string";}testHeap()

3.png

从图中可以看出函数执行后堆中变量分配情况。小数、字符串、symbol 都开辟了堆空间,说明分配在堆中。

有两个相同的”my-string”字符串,但并没有重复开辟两个字符串空间,因为 v8 内部存在名为 stringTable 的 hashmap 缓存了所有字符串,在 V8 阅读代码并转换为 AST 时,每遇到一个字符串都会换算为一个 hash 值插入到 hashmap 中。所以在我们创建字符串的时候,V8 会先从内存哈希表中查找是否有已经创建的完全一致的字符串,若存在,直接复用。若不存在,则开辟一块新的内存空间存储。这也是为什么字符串是不可变的,修改字符串时需要重新开辟新的空间而不能再原来的空间上作修改。

小整数、boolean、undefined、null、空字符串并没有额外开辟空间,对这些数据类型有两种猜测:

存放在栈空间中;存放在堆中但在系统启动时就已经开辟。

4.png

5.png深入探讨JavaScript中的内存管理

其实 V8 中有一个特殊的原始值子集,称为 Oddball。它们在运行之前由 V8 预先分配在堆上,无论 JavaScript 程序是否实际使用到它们。从整个堆空间查看这些类型的分配,boolean、undefined、null、空字符串分配在堆内存中且属于 Oddball 类型。无论何时分配空间对应的内存地址永远是固定的(空字符串@77、null@71、undefined@67、true@73)。但并未找到小整数,证明函数局部变量小整数存在栈中,但定义在全局中的小整数则是分配在堆中。

同样都是表示 Number 类型,小整数和小数在存储上有什么区别呢?

一般编程语言在区分 Number 类型时需要关心 Int、Float、32、64。在 JavaScript 中统称为 Number,但 v8 内部对 Number 类型的实现可没看起来这么简单,在 V8 内部 Number 分为 smi 和 heapNumber,分别用于存储小整数与小数(包括大整数)。ECMAScript 标准约定 Number 需要被当成 64 位双精度浮点数处理,但事实上一直使用 64 位去存储任何数字在时间和空间上非常低效的,并且 smi 大量使用位运算,所以为了提高性能 JavaScript 引擎在存储 smi 的时候使用 32 位去存储数字而 heapNumber 使用 32 位或 64 位存储数字。

Trae国内版 Trae国内版

国内首款AI原生IDE,专为中国开发者打造

Trae国内版 815 查看详情 Trae国内版

以上是局部变量在函数中的内存分布,接下来验证对象的内存分布。谷歌浏览器无痕模式 Console 中输入以下代码,并在 Class filter 中输入 TestClass 查看其内存分布情况。

function TestClass() {    this.number = 123;    this.number2 = 123;    this.heapNumebr = 123.18;    this.heapNumber2 = 123.18;    this.string = "abc";    this.string2 = "abc";    this.boolean = true;    this.symbol = Symbol('test')    this.undefined = undefined;    this.null = null    this.object = { name: 'pp' }    this.array = [1, 2, 3];}let testobject = new TestClass()

6.png

和上一个案例不同的是内存中多了 smi number 类型。由于对象本身就存储在堆中,所以小整数也存储在堆中。shallow size 大小为 0,证明了小整数虽在堆中却不占内存空间。是什么原因导致小整数不占内存空间?

这和 V8 中使用 指针标记技术 有关,指针标记技术使得指针标记位可以存储地址或者标记值。整数的值直接存储在指针中,而不必为其分配额外的存储空间;对象的值需要开辟额外内存空间,指针中存放其地址。这也导致了对象中的小整数数值相同地址也相同。

|------ 32位架构 -----||_____address_____ w1| 指针|___int31_value____ 0| Smi|---------------- 64位架构 ----------------||________base________|_____offset______ w1| 指针|--------------------|___int31_value____ 0| Smi

V8 使用最低有效位来区分 Smi 和对象指针。对于对象指针,它使用第二个最低有效位来区分强引用和弱引用。

在 32 位架构中 Smi 值只能携带 31 位有效载荷。包括符号位,Int32类型的范围是 -(2^31) ~ 2^31 – 1, 所以Smi的范围实际上是Int31类型的范围(-(2^30) ~ 2^30 – 1)。对象指针有 30 位可用作堆对象地址有效负载。

由于单线程和 v8 垃圾回收机制的限制,内存越大回收的过程中 JavaScript 线程会阻塞且严重影响程序的性能和响应能力,出于性能以及避免空间浪费的考虑,大部分浏览器以及 Node15+ 的内存上限为 4G(4G 刚好是 2^32 byte)。以内存上限为 4G 为例,V8 中的堆布局需要保证无论是 64 位系统还是 32 位系统都只使用32位的空间来储存。在 64 位架构中 Smi 同样使用 31 位有效负载,与 32 位架构保持一致;对象指针使用 62 位有效负载,其中前 32 位表示 base(基址),其值指向 4G 内存中间段的地址。后 32 位的前 30 位表示 offset,指前后 2G 内存空间的偏移量。

v8 可以通过以下代码查看内存上限。

const v8 = require('v8')console.log('heap_size_limit:',v8.getHeapStatistics().heap_size_limit) // 查询堆内存上限设置,不同 node 版本默认设置是不一样

通过设置环境 export NODE_OPTIONS=--max_old_space_size=8192 或者启动时传递 --max-old-space-size(或 --max-new-space-size)参数修改内存上限。

通过以上两个案例,细心的读者可能已经发现 heap number 作为函数私有变量时存在复用但作为对象的属性时不存在复用(地址不相同)。作者猜测函数中的私有变量做了类似字符串的 hashmap 优化,而作为对象属性时为了避免每次修改变量重新开辟空间而导致内存消耗大,无论数值是否相同都会重新开辟空间,修改时直接修改指针所指向的具体值。

以执行函数为例简单概括 JavaScript 的内存模型

7.png

垃圾回收机制及策略

使用完内存我们需要对内存进行释放以及归还,像 C 语言这样的底层语言一般都有底层的堆内存管理接口,比如 malloc() 和 free()。相反,JavaScript 是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时”自动”释放。释放的过程称为 垃圾回收。释放过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行,这让 JavaScript 开发者错误的认为可以不关心垃圾回收机制及策略。

引用计数法

这是最初级的垃圾收集算法。此算法把”对象是否不再需要”简化定义为”对象有没有其他对象引用到它”。假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用清除时,对象A的引用计数器就-1,如果对象A的计算器的值为 0,就说明对象A没有引用了,可以被回收。

但该算法有个限制:无法处理循环引用问题。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。

function f(){  var o = {};  var o2 = {};  o.a = o2; // o 引用 o2  o2.a = o; // o2 引用 o  return "";}f();

标记清除法

这个算法把”对象是否不再需要”简化定义为”对象是否可达”,解决了循环引用的问题。这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,不具备可达性的元素将被回收。可达性指的是一个变量是否能够直接或间接通过全局对象访问到,如果可以那么该变量就是可达的,否则就是不可达。

但标记清除法对比引用计数法 缺乏时效性,只有在有效内存空间耗尽了,V8引擎将会停止应用程序的运行并开启 GC 线程,然后开始进行标记工作。所以这种方式效率低,标记和清除都需要遍历所有对象,并且在 GC 时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的;通过标记清除算法清理出来的内容碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

标记压缩算法

标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。

标记压缩算法解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有一定的影响。

增量标记法

标记压缩算法只解决了标记清除法的内存碎片化问题,但是没有解决停顿问题。为了减少全停顿的时间,V8 使用了如下优化,改进后,最大停顿时间减少到原来的1/6。

增量 GC:GC 是在多个增量步骤中完成,而不是一步完成。并发标记: 标记空间的对象哪些是活的哪些是死的是使用多个辅助线程并发进行,不影响 JavaScript 的主线程。并发清扫/压缩:清扫和压缩也是在辅助线程中并发进行,不影响 JavaScript 的主线程。延迟清扫:延迟删除垃圾,直到有内存需求或者主线程空闲时再删除。

V8引擎垃圾回收策略

JavaScript 中的 垃圾回收策略采用分代回收的思想。Heap(堆)内存中只有新空间(New Space)和旧空间(Old Space)由 GC 管理。

新空间(New Space):新对象存活的地方,驻留在此处的对象称为New Generation(新生代)。Minor GC 作为该空间的回收机制,该空间采用 Scavenge 算法 + 标记清除法。

Minor GC 保持新空间的紧凑和干净,其中有一个分配指针,每当我们想为新的对象分配内存空间时,就会递增这个指针。当该指针达到新空间的末端时,就会触发一次 Minor GC。这个过程也被称为 Scavenger,它实现了 Cheney 算法。由于空间很小(1-8MB 之间)导致 Minor GC 经常被触发,所以这些对象的生命周期都很短,而且 Minor GC 过程使用并行的辅助线程,速度非常快,内存分配的成本很低。新空间由两个大小 Semi-Space 组成,为了区分二者 Minor GC 将二者命名为 from-space 和 to-space。内存分配发生在 from-space 空间,当 from-space 空间被填满时,就会触发 Minor GC。将还存活着的对象迁移到 to-space 空间,并将 from-space 和 to-space 的名字交换一下,交换后所有的对象都在 from-space 空间,to-space 空间是空的。一段时间后 from-space 又被填满时再次触发 Minor GC,第二次存活的对象将会被迁移到旧空间(Old Space),第一次存活下来的新对象被迁移到 to-space 空间,如此周而复始操作就形成了 Minor GC 的过程。

旧空间(Old Space):在新空间(New Space)被两次 Minor GC 后依旧存活的对象会被迁移到这里,驻留在此处的对象称为Old Generation(老生代)。 Major GC 作为该空间的回收机制,该空间采用标记清除、标记压缩、增量标记算法。

V8 根据某种算法计算,确定没有足够的旧空间就会触发 Major GC。Cheney 算法对于小数据量来说是完美的,但对于 Heap 中的旧空间来说是不切实际的,因为算法本身有内存开销,所以 Major GC 使用标记清除、标记压缩、增量标记算法。旧空间分为旧址针空间和旧数据空间:旧指针空间包含具有指向其他对象的指针的对象;旧数据空间包含数据的对象(没有指向其他对象的指针)。

内存泄漏

并不是所有内存都会被回收,当程序运行时由于某种原因未能被 GC 而造成内存空间的浪费称为 内存泄漏。轻微的内存泄漏或许不太会对程序造成什么影响,严重的内存泄漏则会影响程序的性能,甚至导致程序的崩溃。

以下是一些导致内存泄漏的场景

闭包

var theThing = null;const replaceThing = function () {   var originalThing = theThing;     var unused = function () {     if (originalThing)       console.log("hi");   };     theThing = {     longStr: new Array(1000000).join('*'),     someMethod: function () {       console.log("someMessage");     }   };  // 如果在此处添加 `originalThing = null`,则不会导致内存泄漏。};setInterval(replaceThing, 1000);

这是一个非常经典的闭包内存泄漏案例,unused 中引用了 originalThing,所以强制它保持活动状态,阻止了它的回收。unused 本身并未被使用所以函数执行结束后会被 gc 回收。但 somemethod 与 unused 在同一个上下文,共享闭包范围。每次执行 replaceThing 时闭包函数 someMethod 中都会引用上一个 theThing 对象。

意外的全局变量

function foo(arg) {     bar = "隐式全局变量"; }// 等同于:function foo(arg) {     window.bar = "显式全局变量"; }

定义大量的全局变量会导致内存泄漏。在浏览器中全局对象是“ window”。在 NodeJs 中全局对象是“global”或“process”。此处变量 bar 永远无法被收集。

还有一种情况是使用 this 生成全局变量。

function fn () {    this.bar = "全局变量"; // 这里的  this 的指向 window, 因此 bar 同样会被挂载到 window 对象下}fn();

避免此问题的办法是在文件头部或者函数的顶部加上 ‘use strict’, 开启严格模式使得 this 的指向为 undefined。

若必须使用全局变量存储大量数据时,确保用完后设置为 null 即可。

忘记清除定时器

setInterval/setTimeout 未被清除会导致内存泄漏。在执行 clearInterval/clearTimeout 之前,系统不会释放 setInterval/setTimeout 回调函数中的变量,及时释放内存就需要手动执行clearInterval/clearTimeout。

若 setTimeout 执行完成则没有内存泄漏的问题,因为执行结束后就会立即释放内存。

忘记清除事件监听器

当组件挂载事件处理函数后,在组件销毁时不主动将其清除,事件处理函数被认为是需要的而不会被 GC。如果内部引用的变量存储了大量数据,可能会引起页面占用内存过高,造成内存泄漏。

忘记清除 DOM 引用

把 DOM 存储在字典(JSON 键值对)或者数组中,当元素从 DOM 中删除时,而 DOM 的引用还是存于内存中,则 DOM 的引用不会被 GC 回收而需要手动清除,所以存储 DOM 通常使用弱引用的方式。

旧版浏览器和错误扩展

旧版浏览器 (IE6–7) 因无法处理 DOM 对象和 JavaScript 对象之间的循环引用而导致内存泄漏。

有时错误的浏览器扩展可能会导致内存泄漏。

内存泄漏排查

若程序运行一段时间后慢慢变卡甚至崩溃,就要开始排查、定位以及修复内存泄漏,常用的内存泄漏排查方式有四种:

使用 Chrome 浏览器的 Performance 查看是否存在内存泄漏,使用 Memory 定位泄漏源。使用 Node.js 提供的 process.memoryUsage 方法,查看 heapUsed 走势;使用node --inspect xxx.js启动服务并访问chrome://inspect,打开 Memory 定位泄漏源;应用接入 grafana 的前提下,可通过 ab 压测观察 grafana 内存走势。

内存分布对大部分开发者来说都是一个黑盒,v8 中实现的 JavaScript 内存模型非常复杂,99% 的开发都不用去关心,甚至在 ECMAScript 规范中也没有找到任何关于内存布局的信息。若是兴趣使然,完全可以看看 v8 引擎的源码。在工作中如果你已经开始专研 JavaScript 内存分布的问题,说明你有能力开始编写更底层的语言了。

本文并非在阅读源码的情况下作出的结论,而是根据内存分析工具结合现有的理论作出的结论,若有不足之处欢迎纠正。

更多编程相关知识,请访问:编程教学!!

以上就是深入探讨JavaScript中的内存管理的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/541500.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
《原神》流浪者圣遗物最强搭配指南
上一篇 2025年11月9日 15:30:11
原神赤沙之杖突破材料是什么 赤沙之杖突破材料一览
下一篇 2025年11月9日 15:30:15

相关推荐

  • 悟空浏览器网页加载不出来一直转圈_悟空浏览器页面无限加载问题解决方法

    悟空浏览器网页加载不出来一直转圈_悟空浏览器页面无限加载问题解决方法悟空浏览器网页加载不出来一直转圈_悟空浏览器页面无限加载问题解决方法悟空浏览器网页加载不出来一直转圈_悟空浏览器页面无限加载问题解决方法悟空浏览器网页加载不出来一直转圈_悟空浏览器页面无限加载问题解决方法

    首先清除浏览器缓存与数据,再检查网络环境并尝试切换Wi-Fi或蜂窝数据,接着修改DNS为8.8.8.8等公共地址,然后禁用插件或脚本,最后更新或重置悟空浏览器以解决页面转圈问题。 如果您尝试在悟空浏览器中访问某个网页,但页面一直转圈无法加载完成,则可能是由于网络连接、浏览器缓存或DNS解析等问题导致…

    2026年10月3日 • 用户投稿
    200
  • Craft CMS 元素预加载:自定义参数与 PHP 数组语法陷阱解析

    本文深入探讨了在 Craft CMS 中为预加载(Eager-Loaded)元素定义自定义参数的方法,重点解决了一个常见的 PHP 语法错误:在 with() 方法中为关联元素传递参数时,误用 JavaScript 风格的对象字面量 {} 而非正确的 PHP 数组语法 []。通过具体示例,文章演示了…

    2026年10月3日
    100
  • 2025 年 Python 现状:83% 仍在运行旧版,Python Web 开发复兴

    2025 年 Python 现状:83% 仍在运行旧版,Python Web 开发复兴2025 年 Python 现状:83% 仍在运行旧版,Python Web 开发复兴2025 年 Python 现状:83% 仍在运行旧版,Python Web 开发复兴2025 年 Python 现状:83% 仍在运行旧版,Python Web 开发复兴

    第八届年度 python 开发者调查现已正式发布,基于来自全球超过 30,000 名 python 开发者的反馈。本次调研由 python software foundation 联合 jetbrains 的 pycharm 团队共同完成。 2025 年 Python 现状(精简速览)半数 Pyth…

    2026年10月3日 • 用户投稿
    200
  • 解决复选框数据无法插入数据库的问题:系统化调试指南

    本文旨在解决Web应用中复选框数据无法成功插入数据库的问题。我们将深入探讨常见错误原因,并提供一套系统化的调试策略,包括利用XDebug进行代码追踪、检查PHP错误日志、直接验证数据库状态以及排查数据库连接配置,确保开发者能够高效定位并解决数据插入失败的根本原因。 理解问题:复选框数据插入失败的常见…

    2026年10月2日
    100
  • 反转字符串绕杀软

    反转字符串绕杀软反转字符串绕杀软反转字符串绕杀软反转字符串绕杀软

    在github有一个ssi的项目,项目地址如下:https://github.com/dimopouloselias/simpleshellcodeinjector/他可以实现shellcode的远程加载(十六进制),当然ldrakura也曾经搞过类似的项目(我偷电瓶车养你啊),当时还是一次项目时绕…

    2026年10月2日 • 用户投稿
    100
  • 优化PHP框架的性能表现_通过ThinkPHP实现php框架怎么用的调优

    启用生产模式、配置与路由缓存、优化数据库查询、合理使用页面缓存是提升ThinkPHP性能的关键。通过关闭调试模式、生成配置缓存文件、设置数据库索引与查询缓存、开启模板及页面缓存,可显著降低资源消耗、加快响应速度,实现高效稳定的Web应用运行。 提升PHP框架的性能,关键在于减少资源消耗、加快响应速度…

    2026年10月2日
    500
  • Win10 Chromium Edge新权限:阻止不安全内容

    Win10 Chromium Edge新权限:阻止不安全内容Win10 Chromium Edge新权限:阻止不安全内容Win10 Chromium Edge新权限:阻止不安全内容Win10 Chromium Edge新权限:阻止不安全内容

    和市面上多数其他浏览器一样,基于chromium内核的microsoft edge已经内置了一些规则,用于针对我们访问的每一个网站来设定权限。当然,为了提高操作便捷性,我们也能为所有页面统一配置规则,这样就能保证网站对文件、摄像头、麦克风等资源的访问能够得到妥善处理。 近期,微软在其最新的Canar…

    2026年10月2日 • 用户投稿
    100
  • Js处理滚动条和日期框

    Js处理滚动条和日期框Js处理滚动条和日期框Js处理滚动条和日期框Js处理滚动条和日期框

    文章总览图 此思维导图中的语法要结合文章内容来看,切记不可死读书,要活学活用。 Js处理1.常用的元素基本操作是什么? 四个基本操作。 1.send_keys() 2.click() 3.获取元素的文本内容。 4.获取元素的属性。 find_element_by_xpath()是我们的元素对象。 .…

    2026年10月2日 • 用户投稿
    200
  • JavaScript数据属性值安全转换为数字的专业指南:避免NaN的策略

    本教程详细阐述了在JavaScript中如何安全地将HTML数据属性(data attributes)的值转换为数字,并确保在值无效时返回0,从而有效避免在计算中出现NaN。文章重点介绍了使用parseFloat()而非Number()进行转换,并强调了将|| 0操作符正确放置在转换函数外部的关键性…

    2026年10月2日
    100
  • 夸克浏览器如何禁用JavaScript_夸克浏览器禁用网页JS脚本设置步骤

    夸克浏览器如何禁用JavaScript_夸克浏览器禁用网页JS脚本设置步骤夸克浏览器如何禁用JavaScript_夸克浏览器禁用网页JS脚本设置步骤夸克浏览器如何禁用JavaScript_夸克浏览器禁用网页JS脚本设置步骤夸克浏览器如何禁用JavaScript_夸克浏览器禁用网页JS脚本设置步骤

    1、可在夸克浏览器中通过高级设置关闭JavaScript以提升隐私保护并防止恶意脚本;2、启用无痕模式可临时限制脚本运行;3、通过自定义过滤规则可屏蔽特定JS文件加载。 如果您在浏览网页时遇到页面加载异常或希望提升隐私保护,可能需要禁用JavaScript来阻止某些脚本运行。夸克浏览器提供了对JS脚…

    2026年10月2日 • 用户投稿
    100
  • 隐形王牌:揭秘研祥智能TRW-8271为何是轨交系统的智慧中枢

    隐形王牌:揭秘研祥智能TRW-8271为何是轨交系统的智慧中枢隐形王牌:揭秘研祥智能TRW-8271为何是轨交系统的智慧中枢隐形王牌:揭秘研祥智能TRW-8271为何是轨交系统的智慧中枢隐形王牌:揭秘研祥智能TRW-8271为何是轨交系统的智慧中枢

    想象一下,在城市地底穿梭不息的地铁与高速飞驰的列车深处,隐藏着一颗冷静而智慧的“大脑”。它从不喧哗,却始终维系着千万人出行的安全节奏。 这颗大脑,正是研祥智能专为轨道交通量身打造的车载%ignore_a_1%上架型整机——TRW-8271。一款将极致可靠性深植血脉的“隐形王牌”。 极致可靠,是一种「…

    2026年10月2日 • 用户投稿
    300
  • TP中JS如何获取模板变量(示例详解)

    下面thinkphp框架教程栏目将给大家介绍php框架中js优雅获取模板变量的方式,希望对需要的朋友有所帮助! PHP框架中JS优雅获取模板变量的方式 在使用PHP框架(本文以ThinkPHP为例)进行页面开发的时候,经常遇到需要将控制器方法中的模板变量代入到页面JS内操作的情况,常见的方式如: l…

    2026年10月2日
    100
  • PHP文件上传系统:如何通过外键将文件精确关联到特定用户

    本教程详细阐述如何在PHP文件上传系统中,通过引入数据库外键(user_id)将上传的QR码图片与特定用户关联。内容涵盖数据库表结构设计、前端表单改造、后端文件处理逻辑及用户专属内容展示,确保文件上传的精确归属和管理。 1. 理解核心问题与解决方案 在多用户或多角色(如员工为用户上传)的应用场景中,…

    2026年10月2日
    100
  • 菜鸟 学注册机编写之 Android app

    菜鸟 学注册机编写之 Android app菜鸟 学注册机编写之 Android app菜鸟 学注册机编写之 Android app菜鸟 学注册机编写之 Android app

    0x00前言 环境及工具: 手机    Nexus 4(己root) 系统版本   Android 5.01 工具    AndroidKiller_V1.2     关于Android平台app注册机的编写网上文章还比较少,而在Windows平台上这方面的教程己经很多了,今天将以一个简单的app为…

    2026年10月1日 • 用户投稿
    200
  • 因Edge文件权限与IE发生冲突可导致XXE攻击

    因Edge文件权限与IE发生冲突可导致XXE攻击因Edge文件权限与IE发生冲突可导致XXE攻击因Edge文件权限与IE发生冲突可导致XXE攻击因Edge文件权限与IE发生冲突可导致XXE攻击

    安全研究员john page在3月27日向发现了微软的一个xxe漏洞并向其报告了具体情况,随后于4月10日发布了漏洞详情。目前,虽然微软还未修复该漏洞,但已发布了一个微密码,可拒绝远程攻击者泄漏本地文件以及限制在本机上的活动。 关于该漏洞,研究人员专门发布了一个视频用于展示如何利用: 漏洞简介 当用…

    2026年10月1日 • 用户投稿
    300
  • Safari浏览器视频加载失败怎么办_Safari视频无法播放问题解决方法

    Safari浏览器视频加载失败怎么办_Safari视频无法播放问题解决方法Safari浏览器视频加载失败怎么办_Safari视频无法播放问题解决方法Safari浏览器视频加载失败怎么办_Safari视频无法播放问题解决方法Safari浏览器视频加载失败怎么办_Safari视频无法播放问题解决方法

    首先清除Safari缓存与网站数据,检查并关闭可能屏蔽视频的扩展程序,确保已启用JavaScript,尝试在无痕浏览模式下播放视频,更新系统与浏览器至最新版本,并更换为公共DNS服务器如8.8.8.8或223.5.5.5以改善解析问题。 如果您在使用Safari浏览器时遇到视频无法加载或播放的问题,…

    2026年10月1日 • 用户投稿
    100
  • 谷歌浏览器页面图片无法显示如何修复

    谷歌浏览器页面图片无法显示如何修复谷歌浏览器页面图片无法显示如何修复谷歌浏览器页面图片无法显示如何修复谷歌浏览器页面图片无法显示如何修复

    当您在谷歌浏览器中浏览网页时,如果遇到图片无法正常显示,出现空白占位符或加载失败的图标,这通常是由于浏览器设置、缓存数据损坏、扩展程序干扰或JavaScript被禁用所导致的。本文将提供一套详细的排查方案,通过逐步检查和调整,帮助您恢复网页图片的正常显示。 检查图片显示设置 1、首先,最直接的原因可…

    2026年9月30日 • 用户投稿
    200
  • R包降级也不全是那么简单

    R包降级也不全是那么简单R包降级也不全是那么简单R包降级也不全是那么简单R包降级也不全是那么简单

    如果你是windows用户,可能会遇到make缺失的错误,如下图所示: 尽管我已经多年未使用Windows,但解决这个问题其实非常简单,只需通过谷歌搜索即可找到答案: 实际上,这是由于rtools缺失导致的问题。Windows用户只需下载适合的rtools版本并安装即可解决。 下载并安装Rtools…

    2026年9月30日 • 用户投稿
    200
  • 如何利用MySQL和JavaScript开发一个简单的任务管理器

    如何利用MySQL和JavaScript开发一个简单的任务管理器如何利用MySQL和JavaScript开发一个简单的任务管理器如何利用MySQL和JavaScript开发一个简单的任务管理器如何利用MySQL和JavaScript开发一个简单的任务管理器

    如何利用MySQL和JavaScript开发一个简单的任务管理器 概述:任务管理器是一种常见的应用程序,它可以帮助我们组织和跟踪日常任务的完成情况。在本文中,我们将学习如何利用MySQL和JavaScript来开发一个简单的任务管理器。该管理器将具有添加、编辑和删除任务的功能,同时具有任务列表的显示…

    2026年9月30日 • 用户投稿
    200
  • Sublime高阶用法详解教程|轻松驾驭各种开发场景

    Sublime高阶用法详解教程|轻松驾驭各种开发场景Sublime高阶用法详解教程|轻松驾驭各种开发场景Sublime高阶用法详解教程|轻松驾驭各种开发场景Sublime高阶用法详解教程|轻松驾驭各种开发场景

    掌握sublime text高阶功能可显著提升开发效率,具体包括:1. 多光标编辑:通过alt+点击或ctrl + shift + l添加多光标,实现批量修改代码;2. 快速跳转和搜索:使用ctrl + p打开文件、ctrl + r跳转函数、ctrl + ;跳转书签,快速定位内容;3. 自定义快捷键…

    2026年9月30日 • 用户投稿
    000

发表回复

登录后才能评论
关注微信