闲云野记:24915
一、前言
本次回顾3个知识点:进程地址空间、JavaScript项目的影响优化、Vue 3 的应用路由。
二、进程地址空间
进程=内核数据结构(task_struct)+代码(只读的)和数据 ;进程具有独立性:多进程之间独享各种资源,运行期间互不干扰。
进程地址空间假设从进程的视角看,就是进程运行时所用到的虚拟地址的集合。这段地址空间中自下而上,地址是增长的,栈是向地址减小的方向增长(先使用高地址)。堆是向地址增长的方向增长(先使用低地址)。堆栈之间的共享区主要是用来加载动态库。
程序运行时,操作系统会给每一个进程都创建一个独立的虚拟地址空间,每一个进程的地址空间都是整个内存的大小。这时操作系统会让进程觉得整个内存都属于这个进程,实际上的内存当然不能全部都给到同一个进程。当进程发出超过的内存实际的大小的需求时,操作系统就会拒绝分配内存。
在操作系统中,进程地址空间中的地址通常又被称为线性地址,因为它是从全0到全1依次增加,顺序编址的,故这些地址也被称为虚拟地址,或者逻辑地址。对于物理内存,硬件存储中的每一个字节都有自己的地址,这个地址就是物理地址。
综上,进程地址空间本质是进程看待内存的方式所抽象出来的一个概念,对应内核中的:struct mm_struct,每个进程对应一个,都认为自己独占系统内存资源。其中,task_struct,mm_struct和页表构成了整个地址空间到物理空间和进程的交互媒介。因此,进程就是加载进内存的程序,由进程常见的数据结构(struct task_struct(控制块) && struct mm_struct(地址空间))和代码数据组成。
根据代码来看,地址空间区域划分的本质是将线性地址空间划分成为一个一个的area,[start,end](代码区,初始化区… …)。虚拟地址本质是在[start,end]之间的连续地址叫做虚拟地址,多个进程就会有多个 task_struct 结构体,对应进程也都有各自的进程地址空间mm_struct。且在task_struct中也会有一个指针指向mm_struct 结构体,当可执行程序运行起来时将代码和数据加载到内存当中。
代码数据加载到内存中过程:进程先将自己的代码和数据首先放在虚拟地址空间的对应区域(堆,栈… …),然后通过页表映射到物理内存:其中,页表的主要工作是将虚拟地址转化为物理地址,也就是物理地址到虚拟地址的映射。当经过页表的转换后,最终我们的可执行程序的代码和数据可以加载到物理内存的任意位置。最终只需要建立代码和数据与物理内存之间的映射关系,就可以通过虚拟地址找到物理内存中的真实地址。页表的存在也使得随机的物理内存变为有序。因为每一个进程都有自己独立的进程地址空间,也都有自己独立的页表,虽然进程的虚拟地址可以一样,但是他们虚拟地址映射的物理地址可能不一样。通过改变页表对应的物理地址映射就可以变更不同的代码和数据。如果这里物理地址也一样的话,可能用到了写时拷贝,但这时,读写权限是不一样 的。对于不同进程虚拟地址在页表中映射的物理地址可能相同,但绝大部分都是不会相同的,但是父子进程继承的话,就有可能会相同,因为子进程会默认继承父子进程的大部分代码和数据(pid,ppid等不会继承)。因为这里用到了写时拷贝技术,写时拷贝就是等到修改数据时猜真正分配内存空间,这是对程序的性能优化,可以延迟甚至避免内存的不必要拷贝。
那应用直接访问物理内存会怎样呢?如果进程直接访问物理内存,那么看到的地址就是物理地址,而语言中有指针,如果指针越界了,一个进程的指针指向了另一个进程的代码和数据,那么进程的独立性,便无法保证。因为物理内存暴露,其中就有可能有恶意程序直接通过物理地址,进行内存数据的篡改,即使操作系统不让改,也可以读取。有了页表的存在,虚拟地址到物理地址的一个转化,由操作系统来完成的,同时编译机制也可以帮系统进行合法性检测。其实页表也是一种权限管理,当你对数据区进行映射时,数据区是可以读写的,相应的在页表中的映射关系中的权限就是可读可写,但是当你对代码区和字符常量区进行映射时,因为这两个区域是只读的,相应的在页表中的映射关系中的权限就是只读,如果你对这段区域进行了写,通过页表当中的权限管理,操作系统就直接就将这个进程干掉。所以进程地址的存在也使得可以通过start和end以及页表的权限来判断指针是否合法访问。从而达到了将内存管理和进程管理进行解耦。
有了进程地址空间,当一个进程需要资源的时候,通过页表映射去要就可以了。内存管理就只需要知道哪些内存区域(配置)是无效的,哪些是有效的(被页表映射的就是有效的,没有被页表映射的就是无效的),当一个进程退出时,它的映射关系也就没了,此时没有了映射关系,物理内存这里就将该进程的数据设置为无效。
三、JavaScript项目中的影响和优化
1)JavaScript文件的大小是影响页面加载时间的关键因素之一。可通过去除代码中的注释、空格、换行符等不必要的字符,以及利用工具如UglifyJS、Terser等进行代码压缩,可以大幅度减少文件体积,进而提升加载速度。经验表明,一个未经优化的JavaScript文件大小为1MB,经过压缩后可能减小到300KB,对于带宽有限或网络状况不佳的用户来说,这种提升尤为明显。根据HTTP Archive的数据,全球平均的JavaScript文件大小约为350KB,而通过压缩,平均可以节省约20%的带宽消耗;
2)加载方式:传统的同步加载方式会阻塞页面的渲染,需直到所有资源加载完成。而异步加载(如使用\u003Cscript async>标签)和懒加载(如Intersection Observer API)技术可以实现在页面需要时才加载相应的资源,从而减少对首屏渲染时间的影响。在一个包含多个图片和视频的新闻网站上,通过懒加载技术,只有当用户滚动到图片或视频的位置时,它们才会开始加载,这样不仅提升了页面加载速度,还减少了不必要的带宽消耗。一项针对大型电商网站的研究表明,采用懒加载技术后,页面加载时间平均缩短了30%,用户满意度显著提升。
3)合理配置HTTP缓存头部(如Cache-Control、Expires等),可以让浏览器缓存静态资源,在下次访问时直接从本地加载,无需再次向服务器请求。这不仅可以减少服务器的负载,还能显著提升页面加载速度。一个常见的做法是为静态资源(如CSS、JavaScript、图片等)设置合理的缓存策略,如“max-age=31536000”(表示缓存一年)。这样,用户在一年内再次访问网站时,这些资源都可以直接从浏览器缓存中获取。据CDN提供商Akamai的报告显示,通过合理利用浏览器缓存,平均可以减少约70%的带宽消耗和85%的页面加载时间。
4)减少DOM操作,Dom是JavaScript中常见的性能瓶颈之一,频繁的DOM操作会导致浏览器频繁地进行重排(reflow)和重绘(repaint),从而影响页面性能。通过减少DOM操作次数、使用DocumentFragment、优化选择器等方式,可以有效提升性能。在一个需要动态添加大量元素的页面上,可以先将所有元素添加到DocumentFragment中,然后再一次性将DocumentFragment添加到DOM树中。这样可以减少重排和重绘的次数,提升性能。一项针对复杂单页应用(SPA)的性能测试表明,通过优化DOM操作,页面渲染时间平均缩短了25%。
四、Vue 3 应用路由
Vue 3 中,路由指的是应用程序的导航系统,允许你在不同的视图或页面之间进行切换。通过 vue-router 插件,你可以定义路由规则,将 URL 路径映射到 Vue 组件,实现页面间的跳转和状态管理。使用路由,用户可以在应用中导航不同的视图,同时保持浏览器的历史记录。
它有2种工作模式,其中,history模式利用 了HTML5 的 History API (pushState, replaceState, 和 popState 事件) 来管理路由。浏览器的 URL 不包含 # 符号,而是直接显示路由的路径部分。但是需要对服务器进行配置,以确保所有路由请求都返回应用的入口 HTML 文件。这种模式提供了更清晰的 URL:没有 # 符号,URL 更加整洁和符合标准。从而可支持更复杂的功能:支持浏览器的前进、后退功能,与传统的浏览器行为一致。但缺点是需要服务器配置:必须通过配置服务器以处理路由,避免 404 错误。2)Hash模式,它使用 URL 的 # 符号后面的部分来来表示路由状态。浏览器不会将 # 后面的部分发送到服务器,所有的路由切换都在客户端完成。Hash模式的兼容性要更好,因几乎所有浏览器都支持哈希模式,且不需要额外的服务器配置,且无需配置服务器即可处理路由。缺点是它的URL 显得不美观,因URL 中含有 # 符号,这可能会影响用户体验和 SEO。另外会存在某些功能限制:如部分工具或库可能对哈希 URL 的支持较差。
五、Nginx会话保持
1、IP Hash:适合简单场景,但对网络变化敏感;
2、Cookie:最常用、灵活,适合大部分会话保持需求。
3、 URL 参数:适合特定业务场景,例如 URL 中带有用户标识符。
4、 第三方模块:基于 nginx-sticky-module
模块实现会话保持,https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng.git,其中NGINX中启用sticky
;后此该指令开启会话保持,模块会自动为每个客户端生成一个基于 Cookie 的哈希值,并将请求路由到同一台服务器。该配置基于 nginx-sticky-module
,它会为客户端设置一个名为 route
的 Cookie,当然也可以自己指定,后续请求将通过该 Cookie 进行会话保持;适用于需要更多控制或特殊功能的场景。