查看原文
其他

JS 会出现内存泄漏问题么?

编程导航和鱼友们 面试鸭 2024-03-29

大家好呀,今天是编程导航 30 天面试题挑战的第二十七天,一起来看看今天有哪些优质面试题吧。

后端

题目一

如何用 Redis 中的 HyperLogLog 统计页面 UV?

官方解析

在 Redis 中,HyperLogLog 是一种基数估算算法,可以用于快速计算一个集合中的不同元素数量的近似值。

在使用 HyperLogLog 统计页面 UV 时,我们可以将每个访问者的 IP 地址作为一个元素加入到 HyperLogLog 中,然后通过计算 HyperLogLog 中元素数量的近似值来估计页面的唯一访问者数量。

以下是一个示例代码,展示如何使用 Redis 的 HyperLogLog 实现页面 UV 统计:

import redis

# 连接 Redis 服务器
r = redis.Redis(host='localhost', port=6379)

# 记录访问者 IP 地址
ip_address = '192.168.0.1'

# 将 IP 地址加入到 HyperLogLog 中
r.pfadd('page_views', ip_address)

# 获取 HyperLogLog 中元素数量的近似值
uv = r.pfcount('page_views')

# 输出页面 UV
print('页面 UV:', uv)

在上面的示例代码中,我们使用 Redis 的 pfadd() 方法将每个访问者的 IP 地址加入到名为 page_views 的 HyperLogLog 中。然后,我们使用 pfcount() 方法获取 page_views 中元素数量的近似值,并将其作为页面 UV 的估计值输出到控制台。

需要注意的是,由于 HyperLogLog 是一种基数估算算法,其结果是一个近似值,并不是精确值。因此,在实际使用中,需要根据具体情况调整参数和精度等级,以获得更准确的结果。

鱼友的精彩回答

维萨斯的回答

HyperLogLog 是用于做基数统计的结构,在输入量非常大的时候,非常好用

  • 12k的大小可以记录 2^64 次方个不同元素
  • 误差极小 0.81%

我用的可是上流的 SpringBoot,为什么要用 Jedis?

使用 HyperLogLo g统计 UV

Application.yaml

server:
  port: 8080
spring:
  redis:
    host: 111.111.111.111
    port: 6379
    database: 0
    password: password

IpCountService

public void IPCount() {
    //... 得到访问的 IP 地址
    String ip = 得到 ip;
    Long hll = redisTemplate.opsForHyperLogLog().add("hll", ip);
    //... 打日志等后续操作
}
public long uv() {
        // PFCOUNT
        return redisTemplate.opsForHyperLogLog().size("hll");
}

CodeJuzi 的回答

使用 Redis 中的 HyperLogLog 统计页面 UV?
import redis.clients.jedis.Jedis;

public class PageUVCounter {

    private Jedis jedis;

    public PageUVCounter(Jedis jedis) {
        this.jedis = jedis;
    }

    public void countUV(String pageName, String userId) {
        String hllKey = "uv:" + pageName;
        jedis.pfadd(hllKey, userId);
    }

    public long getUV(String pageName) {
        String hllKey = "uv:" + pageName;
        return jedis.pfcount(hllKey);
    }

}

使用 Jedis 客户端实现了 PageUVCounter 类。countUV 方法用于统计页面UV,它使用Redis的 pfadd 命令将用户 ID 添加到HyperLogLog 中。getUV 方法用于获取页面UV,它使用 Redis 的 pfcount 命令获取 HyperLogLog 中的基数计数值,即页面的 UV 数量。

注意的点:
  • HyperLogLog 是有误差的,误差范围在 0.81% 以内,因此在实际使用中需要根据误差范围进行适当的调整。
  • HyperLogLog 中存储的是用户 ID 的哈希值,因此需要确保用户 ID 的哈希分布均匀,以避免误差过大。

heart000_1 的回答

使用 Redis 中的 HyperLogLog 可以很方便地统计页面的 UV(Unique Visitors),其主要步骤如下:

  1. 创建一个 HyperLogLog 数据结构:使用 Redis 的 PFADD 命令创建一个 HyperLogLog 数据结构,用于记录所有访问过该页面的用户的信息。例如,使用以下命令创建一个名为 "page_uv" 的 HyperLogLog:
PFADD page_uv user1 user2 user3

上述命令将 user1、user2 和 user3 这三个用户信息添加到名为 "page_uv" 的 HyperLogLog 中。

  1. 统计 HyperLogLog 的基数:使用 Redis 的 PFCOUNT 命令可以统计 HyperLogLog 的基数,即访问过该页面的唯一用户数。例如,使用以下命令可以统计名为 "page_uv" 的 HyperLogLog 的基数:
PFCOUNT page_uv

  上述命令将返回访问过该页面的唯一用户数。

  1. 定期合并 HyperLogLog:为了保证统计结果的准确性,需要定期合并多个 HyperLogLog。使用 Redis 的 PFMERGE 命令可以将多个 HyperLogLog 合并为一个 HyperLogLog。例如,使用以下命令可以将名为 "page_uv1" 和 "page_uv2" 的 HyperLogLog 合并为一个名为 "page_uv" 的 HyperLogLog:
PFMERGE page_uv page_uv1 page_uv2

  上述命令将名为 "page_uv1" 和 "page_uv2" 的 HyperLogLog 合并为一个名为 "page_uv" 的 HyperLogLog,用于统计访问该页面的唯一用户数。

需要注意的是,使用 HyperLogLog 统计 UV 可能会出现误差,误差的大小取决于 HyperLogLog 的精度和数据规模。为了减小误差,可以增加 HyperLogLog 的精度(通过增加 HyperLogLog 的位数),或者将多个 HyperLogLog 定期合并。

题目二

有哪些常见的消息队列模型?分别适用于什么场景?

官方解析

消息队列是一种在分布式系统中用于异步通信的模型,它允许不同的组件通过将消息发送到队列来实现解耦和灵活性。以下是常见的消息队列模型及其适用场景:

  1. 点对点模型(Point-to-Point Model):这种模型中,消息生产者将消息发送到一个队列中,消息消费者从该队列中接收消息。一个消息只会被一个消费者接收,消费者在处理完消息之后会从队列中删除它。这种模型适用于需要保证消息只被一个消费者处理的场景,例如订单系统日志处理等。
  2. 发布-订阅模型(Publish-Subscribe Model):这种模型中,消息生产者将消息发送到一个主题(Topic)中,多个消息消费者可以订阅该主题并接收到所有的消息。每个消息可以被多个消费者接收,消费者在处理完消息之后不会从主题中删除它。这种模型适用于需要将消息广播给多个消费者的场景,例如新闻订阅、实时数据分析等。
  3. 请求-应答模型(Request-Response Model):这种模型中,消息生产者发送一个请求消息到一个队列中,消息消费者从该队列中接收请求并返回一个响应消息。一个请求只会被一个消费者接收并处理,处理完成后返回响应消息给消息生产者。这种模型适用于需要请求-响应模式的场景,例如远程过程调用、微服务通信等。
  4. 推拉模型(Push-Pull Model):这种模型中,消息生产者将消息推送到一个队列中,消息消费者从该队列中拉取消息。消息生产者将消息发送到队列中,消费者按需从队列中拉取消息进行处理。这种模型适用于需要灵活控制消息消费速度的场景,例如数据采集、视频流传输等。 需要注意的是,以上模型并不是完全独立的,实际使用中可以根据具体场景组合使用不同的模型。例如,可以将点对点模型和请求-应答模型结合使用,实现异步的 RPC 调用。另外,在选择消息队列模型时,还需要考虑消息传输的可靠性、顺序性、延迟等因素。

鱼友的精彩回答

小幸运的回答

一、什么是消息队列

我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰、降低系统耦合性。

二、常见模型

ActiveMQ,RabbitMQ,Kafka,RocketMQ

  1. 点对点模型(Point-to-Point Model):也被称为队列模型,消息生产者将消息发送到队列中,然后消息消费者从队列中获取消息并处理。适用于需要精确传递消息的场景,如订单处理、任务调度等。
  2. 发布/订阅模型(Publish/Subscribe Model):也被称为主题模型,消息生产者将消息发送到主题中,然后主题将消息广播给所有订阅该主题的消费者。适用于需要将消息广播给多个消费者的场景,如新闻订阅、实时数据更新等。
  3. 管道模型(Pipes and Filters Model):消息生产者将消息发送到管道中,然后管道中的过滤器依次处理消息并将其发送给下一个过滤器。适用于需要将消息按照一定的处理流程进行处理的场景,如日志处理、数据清洗等。

三、具体应用场景

  1. RabbitMQ:高吞吐量的消息队列;多种语言客户端库支持;支持多种消息协议;支持复杂的路由规则;支持消息确认机制;适合任务队列、日志处理、消息通信等场景。
  2. Apache Kafka:高吞吐量、支持百万级别的消息每秒处理能力;分布式、高可靠、可扩展;支持持久化存储消息;数据复制至多个副本,保证数据可靠性;适合大规模数据流处理、日志系统等场景。
  3. Apache ActiveMQ:完全支持 JMS 规范,具有良好的跨语言支持;支持多种消息协议和多种持久化方式;具有较强的安全机制和集群管理能力;适合企业级应用、金融行业等场景。
  4. Apache RocketMQ:高吞吐量,低延迟,亿级消息堆积能力;支持事务消息、定时消息等高级特性;支持数据双写,保证数据可靠性;适合大规模数据流处理、金融支付等高可靠性场景。

题目三

有几台机器存储着几亿的淘宝搜索日志,假设你只有一台 2g 的电脑,如何选出搜索热度最高的十个关键词?

官方解析

对于 top k 类文本问题,通常比较好的方案是【分治+hash/trie 树+小顶堆】,即先按照 hash 方法把数据集分解成多个小数据集,然后使用 trie 树或者 hash 统计每个小数据集中的词频,随后用小顶堆求出每个数据集中出现频率最高的前 K 个数,最后在所有 top K 中求出最终的 top K

拆分成 n 多个文件:以首字母区分,不同首字母放在不同文件,如果某个文件长度仍过长,继续按照次首字母进行拆分。这样一来,每个文件的每个数据长度基本相同且首字母相同,就能保证数据被独立的分为了 n 个文件,且各个文件中不存在关键词的交集。

分别词频统计:对于每个文件,使用 hash 或者 Trie 树进行进行词频统计

小顶堆排序:依次处理每个文件,并逐渐更新最大的十个词

鱼皮的评论

题解更倾向于以原生命令的方式去实现,其实还有很多现成的第三方库,比如 Redisson。这里要关注分布式锁可能出现的各种异常情况。

鱼友的精彩回答

会冒泡的可乐的回答

1.读取搜索日志数据

从几台机器上存储的搜索日志数据中,将数据读取到内存中。由于数据量较大,使用多线程可以提高数据处理的效率。具体的实现方案,可以将搜索日志数据按照固定大小分块,每个线程读取一个分块的数据进行处理,并将处理结果写入共享队列中,最后汇总所有处理结果得到最终的统计结果。

2.对关键词进行统计

将每一条搜索日志按照关键词进行分组,并统计每个关键词出现的次数。可以使用Counter 类来进行关键词统计。

3.选取搜索热度最高的十个关键词

将统计结果按照关键词出现次数进行降序排序,选取出现次数最高的前十个关键词,作为搜索热度最高的十个关键词。可以使用sorted函数来进行排序。

具体代码如下:

import threading
import queue
from collections import Counter

# 定义线程数和分块大小
NUM_THREADS = 4
BLOCK_SIZE = 1000000

# 定义共享队列
result_queue = queue.Queue()

# 定义线程函数
def process_data(block):
    keywords_counter = Counter()
    for line in block:
        keyword = line.strip().split(' ')[1]
        keywords_counter[keyword] += 1
    result_queue.put(keywords_counter)

# 读取搜索日志数据
# 其中search_log.txt是搜索日志数据文件的路径,可以根据实际情况进行修改
with open('search_log.txt''r') as f:
    # 分块读取数据
    while True:
        block = f.readlines(BLOCK_SIZE)
        if not block:
            break
        # 启动线程进行数据处理
        threads = []
        for i in range(NUM_THREADS):
            start_idx = i * (len(block) // NUM_THREADS)
            end_idx = (i + 1) * (len(block) // NUM_THREADS) if i != NUM_THREADS - 1 else len(block)
            t = threading.Thread(target=process_data, args=(block[start_idx:end_idx],))
            threads.append(t)
            t.start()
        # 等待所有线程处理完毕
        for t in threads:
            t.join()

# 汇总处理结果
final_counter = Counter()
while not result_queue.empty():
    counter = result_queue.get()
    final_counter += counter

# 选取搜索热度最高的十个关键词
top_keywords = final_counter.most_common(10)

heart000_1 的回答

假设每个搜索日志的大小为 100 字节,那么几亿条搜索日志的总大小约为 10 GB。由于只有一台 2G 的电脑,因此不能直接将所有搜索日志读入内存进行处理。我们可以采用外排序(External Sorting)的方法来解决这个问题。具体步骤如下:

  1. 将所有搜索日志分成若干个大小相等的块,每个块的大小可以根据内存大小来确定,比如每个块的大小为 100 MB。

  2. 对于每个块,使用哈希表(Hash Table)来统计每个关键词的搜索次数。由于哈希表的大小不能超过内存大小,因此需要对每个块都单独统计。当哈希表无法容纳更多的数据时,将哈希表中的数据写入一个临时文件。

  3. 对于所有临时文件,使用归并排序(Merge Sort)的方法将它们合并成一个大的有序文件。在合并的过程中,统计每个关键词的搜索次数,并将其存入一个大小为 K 的最小堆中,其中 K 为需要选出的热度最高的关键词的个数。最小堆中维护的是搜索次数最大的 K 个关键词。

  4. 当所有临时文件都被合并并处理完毕后,最小堆中剩余的 K 个关键词即为搜索热度最高的十个关键词。

需要注意的是,在进行哈希表统计和归并排序的过程中,需要使用外部存储器(如硬盘)来存储中间结果。因此,外排序算法的时间复杂度主要取决于磁盘读写的速度,而不是计算的速度。为了提高性能,可以使用多台机器来同时进行处理,从而加快处理速度。

前端

题目一

JS 会出现内存泄漏问题么?在哪些情况下可能会出现内存泄漏?

官方解析

JavaScript 也会出现内存泄漏问题。内存泄漏是指一些被分配的内存空间,因为某些原因而无法被垃圾回收机制回收,导致占用内存空间无法被释放,最终会导致程序崩溃。

在 JavaScript 中,内存泄漏通常出现在以下情况:

  1. 意外的全局变量:如果一个变量没有被垃圾回收,那么它的作用域会被认为是全局的,这样就会造成意外的全局变量,导致内存泄漏。
  2. 定时器没有被清除:如果在页面关闭前,某个定时器没有被清除,它所引用的对象就不会被垃圾回收,从而导致内存泄漏。
  3. 闭包:如果一个闭包中引用了外部变量,而这个闭包被其他代码引用,那么它所引用的外部变量就不会被垃圾回收,从而导致内存泄漏。
  4. DOM 引用:如果在页面上有大量的 DOM 元素,而这些元素被 JavaScript 代码所引用,但是却没有被正确地释放,就会导致内存泄漏。
  5. 循环引用:如果两个或多个对象之间形成了循环引用,即相互引用对方,那么它们都不会被垃圾回收,从而导致内存泄漏。

为了避免 JavaScript 中的内存泄漏问题,可以采取以下措施:

  1. 避免意外的全局变量,可以使用 "use strict" 模式,或者在函数中使用 var、let 或 const 声明变量。
  2. 在使用定时器时,一定要记得及时清除定时器。
  3. 尽量避免使用闭包,如果必须使用闭包,要注意不要引用外部变量或者及时清除闭包。
  4. 在操作 DOM 元素时,要及时释放引用,避免 DOM 引用导致内存泄漏。
  5. 避免循环引用,可以在对象不再使用时,手动断开对象之间的引用关系。

鱼友的精彩回答

useGieGie 的回答

JavaScript 也有可能出现内存泄漏问题。内存泄漏是指在程序运行过程中,已经无用的内存没有被及时释放,导致系统可用内存逐渐减少,最终可能会导致程序崩溃。以下是一些可能会导致内存泄漏的情况:

  1. 意外的全局变量:在函数中未使用 var、let 或 const 关键字声明变量,会导致变量被赋值到全局对象上,这样可能会在不必要的情况下占用大量内存空间。
  2. 被遗忘的定时器和回调函数:设置了定时器和回调函数,但在不需要的时候未及时清除,导致它们仍然被保存在内存中。
  3. 闭包:由于闭包会引用包含函数的变量,如果闭包一直存在,这些变量也会一直存在于内存中,无法被垃圾回收。
  4. DOM 引用:由于 DOM 对象很少被自动垃圾回收,如果对 DOM 元素的引用一直存在,则这些元素所占用的内存也无法被释放。来自剪贴板
  5. 循环引用:当两个或多个对象互相引用,并且在程序执行过程中不再需要这些对象时,它们可能会被垃圾回收器忽略,从而导致内存泄漏。 6. 递归会占用大量的内存,如果递归的深度过大,可能会导致内存泄漏。

避免内存泄漏的方法包括:

  1. 避免循环引用。可以在不需要对象时手动解除引用,或者使用 WeakMap 或 WeakSet 等数据结构来避免循环引用。
  2. 及时清除引用:手动清除不再需要的变量、对象和事件监听器等引用,以便 JavaScript 垃圾回收器回收内存。
  3. 避免创建过多的全局变量:全局变量会一直存在于内存中,因此应该尽量减少全局变量的使用,将变量限制在函数或局部作用域内。
  4. 使用闭包:使用闭包可以有效地控制变量的作用域和生命周期,避免变量在不需要的时候一直占用内存。
  5. 避免使用递归:递归会占用大量的内存,如果递归的深度过大,可能会导致内存泄漏。
  6. 使用定时器和事件处理器:在使用定时器和事件处理器时,要及时清除这些定时器和事件处理器,以避免它们一直占用内存。
  7. 使用可复用的对象池:可复用的对象池是一种有效的内存管理技术,它可以在需要时创建对象,而不是一直创建新的对象。这样可以减少内存的使用量,并提高程序的性能。
  8. 使用内存分析工具:使用内存分析工具可以帮助开发者快速定位内存泄漏的问题,并采取相应的措施进行修复。
  9. 避免在 DOM 上创建大量的数据,可以使用虚拟滚动等技术;
  10. 使用事件委托来避免绑定过多的事件;
  11. 避免使用过多的闭包,可以使用节流和防抖等技术来控制函数执行次数。

总之,要避免内存泄漏,需要我们具备良好的编程习惯,及时清除不再需要的引用,合理使用闭包和定时器等技术,以及使用内存分析工具进行监测和调试。

Kristen 的回答

1.内存泄漏

指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

2.JS的回收机制

JS 垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收系统(GC)会按照固定的时间间隔,周期性的执行。

到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没有使用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

(1)标记清除:

js 中常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

function test(){
  var a=10;//被标记,进入环境
  var b=20;//被标记,进入环境
}
test();//执行完毕之后a、b又被标记离开环境,被回收

(2)引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值(function object array)赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。

相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1.当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。

function test(){
  var a={};//a的引用次数为0
  var b=a;//a的引用次数加1,为1
  var c=a;//a的引用次数加1,为2
  var b={};//a的引用次数减1,为1
}
3.哪些操作会造成内存泄露

(1)意外的全局变量引起的内存泄露:

function leak(){
  leak="xxx";//leak成为一个全局变量,不会被回收
}

(2)闭包引起的内存泄漏:

function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=function(){
    //Even if it's a empty function
  }
}

闭包可以维持函数内部局部变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且内部函数——事件回调的引用外暴了,形成了闭包。

解决方法:

①将事件处理函数定义在外部,解除闭包。

//将事件处理函数定义在外部
function onclickHandler(){
  //do something
}
function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=onclickHandler;
}

②在定义事件处理函数的外部函数中,删除对 DOM 的引用。

//在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=function(){
    //Even if it's a empty function
  }
  obj=null;
}

(3)没有清理的 DOM 元素引用:

var elements={
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff(){
    image.src="http://some.url/image";
    button.click():
    console.log(text.innerHTML)
}
function removeButton(){
    document.body.removeChild(document.getElementById('button'))
}

(4)被遗忘的定时器或者回调:

var someResouce=getData();
setInterval(function(){
    var node=document.getElementById('Node');
    if(node){
        node.innerHTML=JSON.stringify(someResouce)
    }
},1000)

这样的代码很常见,如果 id 为 Node 的元素从 DOM 中移除,该定时器仍会存在,同时,因为回调函数中包含对 someResource 的引用,定时器外面的 someResource 也不会被释放。

(5)循环引用引起的内存泄漏

var element=document.getElementById("some_element");
var myObject=new Object();
myObject.e=element;
element.o=myObject;

上面的例子在一个 DOM 元素(element)与一个原生 js 对象(myObject)之间创建了循环引用。其中,变量 myObject。由于存在这个循环引用,即使例子中的 DOM 从页面中移除,它也永远不会被回收。

例如,我们经常会这么做:

window.οnlοad=function outerFunction(){
  var obj=document.getElementById("element"):
  obj.οnclick=function innerFunction(){};
};

obj 引用了 document.getElementById("element"),而 document.getElementById("element") 的 onclick 方法会引用外部环境中的变量,自然也包括 obj,非常隐蔽,这样就造成了循环引用。

最简单的解决办法就是手动解除循环引用:

myObject.e=null; element.o=null;

window.οnlοad=function outerFunction(){ var obj=document.getElementById("element"): obj.οnclick=function innerFunction(){}; obj=null; }; 将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。要注意的是,IE9+ 并不存在引用导致 DOM 内存泄露问题,可能是微软做了优化,或者 DOM 的回收方式已经改变。

4.如何避免内存泄漏

为了避免 JavaScript 中的内存泄漏问题,可以采取以下措施

  • 避免意外的全局变量,可以使用 "use strict" 模式,或者在函数中使用 var、let 或 const 声明变量。
  • 在使用定时器时,一定要记得及时清除定时器。
  • 尽量避免使用闭包,如果必须使用闭包,要注意不要引用外部变量或者及时清除闭包。
  • 在操作 DOM 元素时,要及时释放引用,避免 DOM 引用导致内存泄漏。
  • 避免循环引用,可以在对象不再使用时,手动断开对象之间的引用关系。

题目二

DNS HTTP 缓存有哪些实现方式?什么是协商缓存和强制缓存?

官方解析

HTTP 缓存可以通过以下几种方式进行实现:

  1. 浏览器缓存:浏览器可以将最近请求过的资源保存到本地,下次请求时可以直接从本地读取,从而提高访问速度。
  2. 代理缓存:代理服务器可以缓存响应,减少对原始服务器的请求次数,从而加快响应速度。
  3. 网关缓存:网关可以缓存来自多个原始服务器的响应,然后将响应发送到客户端,从而提高性能。

HTTP 缓存可以分为协商缓存强制缓存两种类型。

强制缓存是指浏览器在请求资源时,不会发送任何请求头,直接从本地缓存中读取资源,从而提高响应速度。常见的实现方式包括:

  1. Expires 头部:指定资源过期的时间,如果在过期时间之前再次请求该资源,浏览器将直接从缓存中读取资源。
  2. Cache-Control 头部:可以指定资源的缓存策略,包括 public、private、no-cache 等,控制浏览器的缓存行为。

协商缓存是指浏览器在请求资源时,会发送一些请求头到服务器,询问服务器资源是否已经发生改变。如果资源未发生改变,服务器将返回 304 状态码,告诉浏览器可以从缓存中读取资源,从而减少了网络带宽的使用。常见的实现方式包括:

  1. Last-Modified / If-Modified-Since 头部:浏览器在请求资源时,会将资源最后修改时间(Last-Modified)发送到服务器,服务器检查资源是否发生变化,如果没有发生变化,返回 304 状态码,否则返回新的资源。
  2. ETag / If-None-Match 头部:服务器可以给每个资源分配一个唯一的标识符(ETag),浏览器在请求资源时,将该标识符发送到服务器,服务器检查资源是否发生变化,如果没有发生变化,返回 304 状态码,否则返回新的资源。

需要注意的是,协商缓存虽然可以减少网络带宽的使用,但是需要服务器进行资源比较,因此会增加服务器的负载

题目三

怎么进行站点内的图片性能优化?

官方解析

站点内的图片性能优化可以从以下几个方面入手:

  1. 图片压缩:通过压缩图片的大小和质量,可以减少图片的加载时间。可以使用图片压缩工具(如 TinyPNG、JPEGmini 等)来压缩图片。
  2. 图片格式优化:选择正确的图片格式可以减少图片的大小。常见的图片格式有 JPEG、PNG 和 GIF,可以根据图片的特点和用途选择合适的格式。 延迟加载:对于页面中的长列表或懒加载场景,可以延迟加载图片,减少页面加载时间。可以使用工具(如 lazysizes、Lozad.js 等)来实现图片的延迟加载。 3.图片预加载:在用户访问页面之前,可以预加载一些重要的图片,从而提高用户体验。可以使用工具(如 PreloadJS、image-preload 等)来实现图片的预加载。
  3. 响应式图片:对于不同大小的屏幕,可以使用不同大小的图片来提高用户体验。可以使用 HTML 标签的 srcset 和 sizes 属性来实现响应式图片。 5.CDN 加速:使用 CDN 可以加速图片的加载速度,缩短图片的响应时间。可以选择静态文件托管服务商(如七牛云、阿里云等)来实现 CDN 加速。
  4. 图片懒加载:对于网页中需要加载大量图片的场景,可以使用图片懒加载技术,即当页面滚动到某个区域时再加载该区域内的图片,从而减少页面加载时间和网络带宽的消耗。可以使用工具(如 lazyload、intersection-observer 等)来实现图片懒加载。

需要注意的是,以上优化策略都是针对特定场景和需求进行优化的,需要根据实际情况选择合适的优化策略。同时,在进行图片优化时,需要保证图片质量和用户体验,并注意不要过度压缩图片,影响用户体验。

总结一下:减少传输时间(减小图片尺寸、网络优化),按需传图(懒加载、延迟加载、响应式)

鱼友的精彩回答

玄德ь 的回答

优化站点内的图片性能可以尝试以下方法:

  1. 采用图片压缩技术,减少图片大小,提升网页加载速度; 2.使用矢量图,替换位图;
  2. 使用CSS Sprites技术,将多个图片组合成一个,减少HTTP请求次数;
  3. 使用图片缓存机制,避免反复请求图片资源;
  4. 使用CDN加速:使用CDN可以将图片缓存到离用户更近的服务器上,从而提高图片的加载速度。
  5. 使用图片懒加载技术,减少无效网络请求;
  6. 使用webP格式代替JPG/PNG格式,提升图片显示质量。

以上是一些常见的图片性能优化方法,根据具体的业务需求和网站特点,可以选择适合的方法进行优化。同时,也可以使用一些工具和技术来辅助进行图片性能优化,例如图片压缩工具、CDN服务、lazy load插件等。

星球活动

1.欢迎参与 30 天面试题挑战活动 ,搞定高频面试题,斩杀面试官!

2.欢迎已加入星球的同学 免费申请一年编程导航网站会员 !

3.欢迎学习 鱼皮最新原创项目教程,手把手教你做出项目、写出高分简历!

加入我们

欢迎加入鱼皮的编程导航知识星球,鱼皮会 1 对 1 回答您的问题、直播带你做出项目、为你定制学习计划和求职指导,还能获取海量编程学习资源,和上万名学编程的同学共享知识、交流进步。

💎 加入星球后,您可以:

1)添加鱼皮本人微信,向他 1 对 1 提问,帮您解决问题、告别迷茫!点击了解详情

2)获取海量编程知识和资源,包括:3000+ 鱼皮的编程答疑和求职指导、原创编程学习路线、几十万字的编程学习知识库、几十 T 编程学习资源、500+ 精华帖等!点击了解详情

3)找鱼皮咨询求职建议和优化简历,次数不限!点击了解详情

4)鱼皮直播从 0 到 1 带大家做出项目,已有 50+ 直播、完结 3 套项目、10+ 项目分享,帮您掌握独立开发项目的能力、丰富简历!点击了解详情

外面一套项目课就上千元了,而星球内所有项目都有指导答疑,轻松解决问题

星球提供的所有服务,都是为了帮您更好地学编程、找到理想的工作。诚挚地欢迎您的加入,这可能是最好的学习机会,也是最值得的一笔投资!

长按扫码领优惠券加入,也可以添加微信 yupi1085 咨询星球(备注“想加星球”):

往期推荐

编程导航,火了!

什么是 AOP?

什么是注册中心?

Spring 支持哪几种事务管理类型?

什么是 Bean 的生命周期?

RPC是什么?




继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存