当前位置:首页 > 科技  > 软件

图解 V8 执行 JS 的过程

来源: 责编: 时间:2023-10-10 18:30:49 159观看
导读本文来分享 V8 引擎执行 JavaScript 的过程和垃圾回收机制。1、JS 代码执行过程在说V8的执行JavaScript代码的机制之前,我们先来看看编译型和解释型语言的区别。(1)编译型语言和解释型语言我们知道,机器是不能直接理解代

Eo128资讯网——每日最新资讯28at.com

本文来分享 V8 引擎执行 JavaScript 的过程和垃圾回收机制。Eo128资讯网——每日最新资讯28at.com

1、JS 代码执行过程

在说V8的执行JavaScript代码的机制之前,我们先来看看编译型和解释型语言的区别。Eo128资讯网——每日最新资讯28at.com

(1)编译型语言和解释型语言

我们知道,机器是不能直接理解代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言:Eo128资讯网——每日最新资讯28at.com

  • 编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果。
  • 解释型语言:需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。

Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多浏览器的 JS 引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。Eo128资讯网——每日最新资讯28at.com

编译型语言和解释器语言代码执行的具体流程如下:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

两者的执行流程如下:Eo128资讯网——每日最新资讯28at.com

  • 在编译型语言的编译过程中,编译器首先会依次对源代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后再生成处理器能够理解的机器码。如果编译成功,将会生成一个可执行的文件。但如果编译过程发生了语法或者其他的错误,那么编译器就会抛出异常,最后的二进制文件也不会生成成功。
  • 在解释型语言的解释过程中,同样解释器也会对源代码进行词法分析、语法分析,并生成抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后再根据字节码来执行程序、输出结果。

(2) V8 执行代码过程

V8 在执行过程用到了解释器和编译器。 其执行过程如下:Eo128资讯网——每日最新资讯28at.com

  • Parse 阶段:V8 引擎将 JS 代码转换成 AST(抽象语法树)。
  • Ignition 阶段:解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息。
  • TurboFan 阶段:编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码。
  • Orinoco 阶段:垃圾回收阶段,将程序中不再使用的内存空间进行回收。

这里前三个步骤是JavaScript的执行过程,最后一步是垃圾回收的过程。下面就先来看看V8 执行 JavaScript的过程。Eo128资讯网——每日最新资讯28at.com

生成抽象语法树

这个过程就是将源代码转换为抽象语法树(AST),并生成执行上下文,执行上下文就是代码在执行过程中的环境信息。Eo128资讯网——每日最新资讯28at.com

将 JS 代码解析成 AST主要分为两个阶段:Eo128资讯网——每日最新资讯28at.com

  • 词法分析:这个阶段会将源代码拆成最小的、不可再分的词法单元,称为 token。比如代码 var a = 1;通常会被分解成 var 、a、=、1、; 这五个词法单元。代码中的空格在 JavaScript 中是直接忽略的,简单来说就是将 JavaScript 代码解析成一个个令牌(Token)。
  • 语法分析:这个过程是将上一步生成的 token 数据,根据语法规则转为 AST。如果源码符合语法规则,这一步就会顺利完成。如果源码存在语法错误,这一步就会终止,并抛出一个语法错误,简单来说就是将令牌组装成一棵抽象的语法树(AST)。

通过词法分析会对代码逐个字符进行解析,生成类似下面结构的令牌(Token),这些令牌类型各不相同,有关键字、标识符、符号、数字等。代码 var a = 1;会转化为下面这样的令牌:Eo128资讯网——每日最新资讯28at.com

Keyword(var)Identifier(name)Punctuator(=)Number(1)

语法分析阶段会用令牌生成一棵抽象语法树,生成树的过程中会去除不必要的符号令牌,然后按照语法规则来生成。下面来看两段代码:Eo128资讯网——每日最新资讯28at.com

// 第一段代码var a = 1;// 第二段代码function sum (a,b) {  return a + b;}

将这两段代码分别转换成 AST 抽象语法树之后返回的 JSON 如下:Eo128资讯网——每日最新资讯28at.com

  • 第一段代码,编译后的结果:
{  "type": "Program",  "start": 0,  "end": 10,  "body": [    {      "type": "VariableDeclaration",      "start": 0,      "end": 10,      "declarations": [        {          "type": "VariableDeclarator",          "start": 4,          "end": 9,          "id": {            "type": "Identifier",            "start": 4,            "end": 5,            "name": "a"          },          "init": {            "type": "Literal",            "start": 8,            "end": 9,            "value": 1,            "raw": "1"          }        }      ],      "kind": "var"    }  ],  "sourceType": "module"}

它的结构大致如下:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

  • 第二段代码,编译出来的结果:
{  "type": "Program",  "start": 0,  "end": 38,  "body": [    {      "type": "FunctionDeclaration",      "start": 0,      "end": 38,      "id": {        "type": "Identifier",        "start": 9,        "end": 12,        "name": "sum"      },      "expression": false,      "generator": false,      "async": false,      "params": [        {          "type": "Identifier",          "start": 14,          "end": 15,          "name": "a"        },        {          "type": "Identifier",          "start": 16,          "end": 17,          "name": "b"        }      ],      "body": {        "type": "BlockStatement",        "start": 19,        "end": 38,        "body": [          {            "type": "ReturnStatement",            "start": 23,            "end": 36,            "argument": {              "type": "BinaryExpression",              "start": 30,              "end": 35,              "left": {                "type": "Identifier",                "start": 30,                "end": 31,                "name": "a"              },              "operator": "+",              "right": {                "type": "Identifier",                "start": 34,                "end": 35,                "name": "b"              }            }          }        ]      }    }  ],  "sourceType": "module"}

它的结构大致如下:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

可以看到,AST 只是源代码语法结构的一种抽象的表示形式,计算机也不会去直接去识别 JS 代码,转换成抽象语法树也只是识别这一过程中的第一步。AST 的结构和代码的结构非常相似,其实也可以把 AST 看成代码的结构化的表示,编译器或者解释器后续的工作都需要依赖于 AST。Eo128资讯网——每日最新资讯28at.com

AST的应用场景:

AST 是一种很重要的数据结构,很多地方用到了AST。比如在 Babel 中,Babel 是一个代码转码器,可以将 ES6 代码转为 ES5 代码。Babel 的工作原理就是先将 ES6 源码转换为 AST,然后再将 ES6 语法的 AST 转换为 ES5 语法的 AST,最后利用 ES5 的 AST 生成 JavaScript 源代码。Eo128资讯网——每日最新资讯28at.com

除了 Babel 之外,ESLint 也使用到了 AST。ESLint 是一个用来检查 JavaScript 编写规范的插件,其检测流程也是需要将源码转换为 AST,然后再利用 AST 来检查代码规范化的问题。Eo128资讯网——每日最新资讯28at.com

除了上述应用场景,AST 的应用场景还有很多:Eo128资讯网——每日最新资讯28at.com

  • JS 反编译,语法解析。
  • 代码高亮。
  • 关键字匹配。
  • 代码压缩。

生成字节码

有了 抽象语法树 AST 和执行上下文后,就轮到解释器就登场了,它会根据 AST 生成字节码,并解释执行字节码。Eo128资讯网——每日最新资讯28at.com

在 V8 的早期版本中,是通过 AST 直接转换成机器码的。将 AST 直接转换为机器码会存在一些问题:Eo128资讯网——每日最新资讯28at.com

  • 直接转换会带来内存占用过大的问题,因为将抽象语法树全部生成了机器码,而机器码相比字节码占用的内存多了很多;
  • 某些 JavaScript 使用场景使用解释器更为合适,解析成字节码,有些代码没必要生成机器码,进而尽可能减少了占用内存过大的问题。

为了解决内存占用问题,就在 V8 引擎中引入了字节码。那什么是字节码呢?为什么引入字节码就能解决内存占用问题呢?Eo128资讯网——每日最新资讯28at.com

字节码就是介于 AST 和机器码之间的一种代码。 需要将其转换成机器码后才能执行,字节码是对机器码的一个抽象描述,相对于机器码而言,它的代码量更小,从而可以减少内存消耗。解释器除了可以快速生成没有优化的字节码外,还可以执行部分字节码。Eo128资讯网——每日最新资讯28at.com

生成机器码

生成字节码之后,就进入执行阶段了,实际上,这一步就是将字节码生成机器码。Eo128资讯网——每日最新资讯28at.com

一般情况下,如果字节码是第一次执行,那么解释器就会逐条解释执行。在执行字节码过程中,如果发现有热代码(重复执行的代码,运行次数超过某个阈值就被标记为热代码),那么后台的编译器就会把该段热点的字节码编译为高效的机器码,然后当再次执行这段被优化的代码时,只需要执行编译后的机器码即可,这样提升了代码的执行效率。Eo128资讯网——每日最新资讯28at.com

字节码配合解释器和编译器的技术就是 即时编译(JIT)。在 V8 中就是指解释器在解释执行字节码的同时,收集代码信息,当它发现某一部分代码变热了之后,编译器便闪亮登场,把热点的字节码转换为机器码,并把转换后的机器码保存起来,以备下次使用。Eo128资讯网——每日最新资讯28at.com

因为 V8 引擎是多线程的,编译器的编译线程和生成字节码不会在同一个线程上,这样可以和解释器相互配合着使用,不受另一方的影响。下面是JIT技术的工作机制:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

解释器在得到 AST 之后,会按需进行解释和执行。也就是说如果某个函数没有被调用,则不会去解释执行它。在这个过程中解释器会将一些重复可优化的操作收集起来生成分析数据,然后将生成的字节码和分析数据传给编译器,编译器会依据分析数据来生成高度优化的机器码。Eo128资讯网——每日最新资讯28at.com

优化后的机器码的作用和缓存很类似,当解释器再次遇到相同的内容时,就可以直接执行优化后的机器码。当然优化后的代码有时可能会无法运行(比如函数参数类型改变),那么会再次反优化为字节码交给解释器。Eo128资讯网——每日最新资讯28at.com

整个过程如下图所示:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

(3)执行过程优化

如果JavaScript代码在执行前都要完全经过解析才能执行,那可能会面临以下问题:Eo128资讯网——每日最新资讯28at.com

  • 代码执行时间变长:一次性解析所有代码会增加代码的运行时间。
  • 消耗更多内存:解析完的 AST 以及根据 AST 编译后的字节码都会存放在内存中,会占用更多内存空间。
  • 占用磁盘空间:编译后的代码会缓存在磁盘上,占用磁盘空间。

所以,V8 引擎使用了延迟解析:在解析过程中,对于不是立即执行的函数,只进行预解析;只有当函数调用时,才对函数进行全量解析。Eo128资讯网——每日最新资讯28at.com

进行预解析时,只验证函数语法是否有效、解析函数声明、确定函数作用域,不生成 AST,而实现预解析的,就是 Pre-Parser 解析器。Eo128资讯网——每日最新资讯28at.com

以下面代码为例:Eo128资讯网——每日最新资讯28at.com

function sum(a, b) {    return a + b;}const a = 666;const c = 996;sum(1, 1);

V8 解析器是从上往下解析代码的,当解析器遇到函数声明 sum 时,发现它不是立即执行,所以会用 Pre-Parser 解析器对其预解析,过程中只会解析函数声明,不会解析函数内部代码,不会为函数内部代码生成 AST。Eo128资讯网——每日最新资讯28at.com

之后解释器会把 AST 编译为字节码并执行,解释器会按照自上而下的顺序执行代码,先执行 const a = 666;  和 const c = 996; ,然后执行函数调用 sum(1, 1) ,这时 Parser 解析器才会继续解析函数内的代码、生成 AST,再交给解释器编译执行。Eo128资讯网——每日最新资讯28at.com

2、垃圾回收

(1)JS 内存管理机制

计算机程序语言都运行在对应的代码引擎上,使用内存过程可以分为以下三个步骤:Eo128资讯网——每日最新资讯28at.com

  • 分配所需要的系统内存空间。
  • 使用分配到的内存进行读或写等操作。
  • 不需要使用内存时,将其空间释放或者归还。

在 JavaScript 中,当创建变量时,系统会自动给对象分配对应的内存,来看下面的例子:Eo128资讯网——每日最新资讯28at.com

var a = 123; // 给数值变量分配栈内存var etf = "ARK"; // 给字符串分配栈内存// 给对象及其包含的值分配堆内存var obj = {  name: 'tom',  age: 13}; // 给数组及其包含的值分配内存var a = [1, null, "str"]; // 给函数分配内存function sum(a, b){  return a + b;}

JavaScript 中的数据分为两类:Eo128资讯网——每日最新资讯28at.com

  • 基本类型:这些类型在内存中会占据固定的内存空间,它们的值都保存在栈空间中,直接可以通过值来访问这些;
  • 引用类型:由于引用类型值大小不固定,栈内存中存放地址指向堆内存中的对象,是通过引用来访问的。

栈内存中的基本类型,可以通过操作系统直接处理;而堆内存中的引用类型,正是由于可以经常变化,大小不固定,因此需要 JavaScript 的引擎通过垃圾回收机制来处理。所谓的垃圾回收是指:JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间。 Javascript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。Eo128资讯网——每日最新资讯28at.com

JavaScript中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续要页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。Eo128资讯网——每日最新资讯28at.com

(2)V8 垃圾回收过程

先来看看 Chrome 浏览器的垃圾回收过程:Eo128资讯网——每日最新资讯28at.com

通过 GC Root 标记空间中活动对象和⾮活动对象

目前 V8 采用的可访问性算法来判断堆中的对象是否是活动对象。这个算法是将一些 GC Root 作为初始存活的对象的集合,从 GC Roots 对象出发,遍历 GC Root 中所有对象:Eo128资讯网——每日最新资讯28at.com

  • 通过 GC Root 遍历到的对象是可访问的,必须保证这些对象应该在内存中保留,可访问的对象称为活动对象。
  • 通过 GC Roots 没有遍历到的对象是不可访问的,这些不可访问的对象就可能被回收,不可访问的对象称为非活动对象。

回收非活动对象所占据的内存

其实就是在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。Eo128资讯网——每日最新资讯28at.com

内存整理

一般来说,频繁回收对象后,内存中就会存在大量不连续空间,这些不连续的内存空间称为内存碎片。当内存中出现了大量的内存碎片之后,如果需要分配较大的连续内存时,就有可能出现内存不足的情况,所以最后一步需要整理这些内存碎片。这步其实是可选的,因为有的垃圾回收器不会产生内存碎片。Eo128资讯网——每日最新资讯28at.com

以上就是大致的垃圾回收流程。目前 V8 使用了两个垃圾回收器:主垃圾回收器和副垃圾回收器。下面就来看看 V8 是如何实现垃圾回收的。Eo128资讯网——每日最新资讯28at.com

在 V8 中,会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放生存时间久的对象:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

新⽣代通常只⽀持 1~8M 的容量,而老生代支持的容量就大很多。对于这两块区域,V8分别使用两个不同的垃圾回收器,以便更高效地实施垃圾回收:Eo128资讯网——每日最新资讯28at.com

  • 副垃圾回收器:负责新生代的垃圾回收。
  • 主垃圾回收器:负责老生代的垃圾回收。

副垃圾回收器(新生代)

副垃圾回收器主要负责新生代的垃圾回收。大多数的对象最开始都会被分配在新生代,该存储空间相对较小,分为两个空间:from 空间(对象区)和 to 空间(空闲区)。Eo128资讯网——每日最新资讯28at.com

新加入的对象都会存放到对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作:首先要对对象区域中的垃圾做标记,标记完成之后,就进入垃圾清理阶段。副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来。这个复制过程就相当于完成了内存整理操作,复制后空闲区域就没有内存碎片了:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域,这种算法称之为 Scavenge 算法,这样就完成了垃圾对象的回收操作。同时,这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

不过,副垃圾回收器每次执行清理操作时,都需要将存活的对象从对象区域复制到空闲区域,复制操作需要时间成本,如果新生区空间设置得太大了,那么每次清理的时间就会过久,所以为了执行效率,一般新生区的空间会被设置得比较小。 也正是因为新生区的空间不大,所以很容易被存活的对象装满整个区域,副垃圾回收器一旦监控对象装满了,便执行垃圾回收。同时,副垃圾回收器还会采用对象晋升策略,也就是移动那些经过两次垃圾回收依然还存活的对象到老生代中。Eo128资讯网——每日最新资讯28at.com

主垃圾回收器(老生代)

主垃圾回收器主要负责老生代中的垃圾回收。除了新生代中晋升的对象,⼀些⼤的对象会直接被分配到老生代里。因此,老生代中的对象有两个特点:Eo128资讯网——每日最新资讯28at.com

  • 对象占用空间大。
  • 对象存活时间间。

由于老生代的对象比较大,若要在老生代中使用 Scavenge 算法进行垃圾回收,复制这些大的对象将会花费较多时间,从而导致回收执行效率不高,同时还会浪费空间。所以,主垃圾回收器采用标记清除的算法进行垃圾回收。Eo128资讯网——每日最新资讯28at.com

这种方式分为标记和清除两个阶段:Eo128资讯网——每日最新资讯28at.com

  • 标记阶段: 从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
  • 清除阶段: 主垃圾回收器会直接将标记为垃圾的数据清理掉。

这两个阶段如图所示:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

对垃圾数据进行标记,然后清除,这就是标记清除算法,不过对一块内存多次执⾏标记清除算法后,会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存,于是又引入了另外一种算法——标记整理。Eo128资讯网——每日最新资讯28at.com

这个算法的标记过程仍然与标记清除算法里的是一样的,先标记可回收对象,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉这一端之外的内存:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

全停顿

我们知道,JavaScript 是单行线语言,运行在主线程上。一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。这种行为叫做全停顿。Eo128资讯网——每日最新资讯28at.com

主垃圾回收器执行一次完整的垃圾回收流程如下图所示:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

在 V8 新生代的垃圾回收中,因其空间较小,且存活对象较少,所以全停顿的影响不大。但老生代中,如果在执行垃圾回收的过程中,占用主线程时间过久,主线程是不能做其他事情的,需要等待执行完垃圾回收操作才能做其他事情,这将就可能会造成页面的卡顿现象。Eo128资讯网——每日最新资讯28at.com

为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,这个算法称为增量标记算法。如下图所示:Eo128资讯网——每日最新资讯28at.com

Eo128资讯网——每日最新资讯28at.com

使用增量标记算法可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样当执行代码时,就不会让用户因为垃圾回收任务而感受到页面的卡顿了。Eo128资讯网——每日最新资讯28at.com

(3)减少垃圾回收

虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂时,垃圾回收所带来的代价较大,所以应该尽量减少垃圾回收:Eo128资讯网——每日最新资讯28at.com

  • 对数组进行优化: 在清空一个数组时,最简单的方法就是给其赋值为[ ],但是与此同时会创建一个新的空对象,可以将数组的长度设置为0,以此来达到清空数组的目的。
  • 对object进行优化: 对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收。
  • 对函数进行优化: 在循环中的函数表达式,如果可以复用,尽量放在函数的外面。

本文链接:http://www.28at.com/showinfo-26-12696-0.html图解 V8 执行 JS 的过程

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 八个优秀开源DevOps工具

下一篇: Java日志管理:选择合适的日志框架记录应用运行状态

标签:
  • 热门焦点
  • MIX Fold3包装盒泄露 新机本月登场

    MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • 如何正确使用:Has和:Nth-Last-Child

    如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 学习JavaScript的10个理由...

    学习JavaScript的10个理由...

    作者 | Simplilearn编译 | 王瑞平当你决心学习一门语言的时候,很难选择到底应该学习哪一门,常用的语言有Python、Java、JavaScript、C/CPP、PHP、Swift、C#、Ruby、Objective-
  • 让我们一起聊聊文件的操作

    让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为“纯欲天花板”的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • 品牌洞察丨服务本地,美团直播成效几何?

    品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现“美团直播”的固定入口。在直播聚合页面,外卖“神枪手”直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • 小米MIX Fold 3下月亮相:今年唯一无短板的全能折叠屏

    小米MIX Fold 3下月亮相:今年唯一无短板的全能折叠屏

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都有新的进展,其中荣耀、三星都已陆续发布了最新的折叠屏旗舰,尤其号荣耀Magi
  • Windows 11发布,微软一改往常对老机型开放的态度

    Windows 11发布,微软一改往常对老机型开放的态度

    距离 Windows 11 发布已经过去一周,在过去一周里,很多数码爱好者围绕其对 Android 应用的支持、对老机型的升级问题展开了激烈讨论。与以往不同的是,在这次大
  • 联想小新Pad Pro 12.6将要推出,搭载高通骁龙 870 处理器

    联想小新Pad Pro 12.6将要推出,搭载高通骁龙 870 处理器

    联想小新Pad Pro 12.6将于秋季新品会上推出,官方按照惯例直接在发布会前给出了机型的所有参数。联想小新 Pad Pro 12.6 将搭载高通骁龙 870 处理器,重量为 5
Top