面试


1.线程和进程的区别

进程是分配资源的最小单元,进程包括多个线程,线程其实对应的就是cpu的核心

线程是操作系统调度的最小单位,多个线程可以共享进程的资源

2.TCP和UDP的区别

1.TCP是需要握手连接的,udp不用直接发送数据包,但是可靠性低

2.相对来说TCP较为安全一点,需要3次握手4次回收,具有丢包重传,流量控制和拥塞控制,数据按顺序的,因为有序列号和应答号

3.udP不用去管 连接的顺序和是否成功到达,需要会更进一步的优化才行,使用与视频流,在线游戏这种

3.网页输入url整个流程

网页上输入URl

首先我们知道 需要对这个url进行解析,我们需要知道这个 url的地址

首先就是 DNS域名解析,

首先是去查看本地缓存里面有没有 没有

通过不同的根,域名,顶级解析器来获得 其中的IP地址

获得IP地址之后应该去传输了,然后是建立连接,使用TCP或者UDP,完成之后浏览器发送http的请求给服务器,(请求头,请求url,请求方法) 服务器通过controller 进行处理 执行业务,然后返回对应的相应,其中包括一些 响应状态码还是起他,

状态码的第一个数字表示响应类别,后面两位数字表示更详细的状态信息。

首先是 传输头分片加上MAC的地址,一起去寻找目的地

然后获得资源就是html前端页面进行渲染这样

4.java集合类

集合类包括 list arraylist linkedlist

5.HashMap

基于哈希表,主要是hashcode()计算

之前是 table+ 链表 后面是 +加上红黑树

首先是检查 容量是否需要扩容, 然后 查看table满了没有,然后放到链表里卖弄,满了转成红黑树,红黑树满了,扩建一般是 0.75 两倍

解决哈希冲突,链表寻址法 + 开放地址(都放在一起,然后 多计算几次hashcode存储)

线性探测,二次探测

再哈希法 当发生冲突时,可以对哈希函数的结果进行重新哈希计算,从而找到一个新的索引位置。这种方法一般和开放地址法结合使用。

扩容 与 重哈希

一致性哈希常用于分布式系统中,它将整个哈希空间视为一个环形结构,避免了数据倾斜问题。它将数据均匀分布到多个桶(节点)中,并在哈希冲突时,通过移动顺时针找到下一个节点。

6. JVM

7.MySQL如何分页;分页数据量大会有什么问题;如何解决

LIMIT page_size OFFSET offset_value;

offset_value = (page_number - 1) * page_size

分页数据量大可能带来的问题

当数据量较大时,使用 LIMITOFFSET 进行分页会遇到以下几个问题:

a. 性能问题

  • OFFSET 会跳过前面的记录,这意味着 MySQL 需要扫描 OFFSET + LIMIT 记录,但实际上只返回 LIMIT 条。因此,当 OFFSET 值很大时,MySQL 仍需要扫描大量数据,造成查询效率下降。
  • 随着 OFFSET 增加,查询的时间复杂度会逐渐变高,特别是在大表中,响应时间可能显著增加。

b. 内存消耗

  • MySQL 在执行查询时,可能会将查询结果放在内存中,特别是当没有合适的索引时。对于大数据集分页,内存消耗会显著增加,可能会影响系统性能。

8.spring设计模式有哪些

创建型模式结构型模式行为型模式

创建型模式(Creational Patterns)

创建型模式关注对象的创建过程,解决对象创建的复杂性,提供更灵活的对象创建方式。

  1. 工厂方法模式(Factory Method)

    • 定义:通过定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法让类的实例化延迟到子类。
    • 场景:当类不知道它所需要创建的对象的确切类型时。
    • 例子:LoggerFactory 根据日志类型创建不同的日志对象。

    在spring中 BeanFactoryApplicationContext 是典型的工厂模式的实现。用于创建和管理 Spring Bean 实例,控制对象的创建过程,使得对象的创建与使用分离。

    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    MyBean myBean = context.getBean(MyBean.class);
  2. 抽象工厂模式(Abstract Factory)

    • 定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

    • 场景:当系统需要独立于对象的具体实现来创建多个相关对象时。

    • 例子:创建不同品牌的家具(如椅子、桌子等),具体品牌由工厂类决定。

      在 Spring 中,BeanFactory 是一个抽象工厂,负责提供一个或多个工厂接口来创建 Bean 实例。

  3. 单例模式(Singleton)

    • 定义:确保某个类只有一个实例,并提供一个全局访问点来访问该实例。

    • 场景:需要确保全局有且只有一个实例时,如数据库连接、日志管理器等。

    • 例子:DatabaseConnection,所有访问数据库的操作共享同一个连接实例。

      Spring 默认情况下 Bean 是单例的(通过 @Scope("singleton") 声明),这意味着每个 Spring 容器中只有一个实例。保证在整个应用程序中,某个类的实例只有一个,减少内存占用,避免频繁创建和销毁对象。Spring 中每个默认 Bean 实例都是单例,生命周期由容器控制。

  4. 原型模式(Prototype)

    • 定义:通过复制现有实例来创建新对象,而不是通过类实例化。
    • 场景:当需要创建的对象是相似或重复时,避免昂贵的对象创建过程。
    • 例子:克隆一份复杂对象,如深度复制具有复杂结构的对象。
  5. 建造者模式(Builder)

    • 定义:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
    • 场景:需要构建复杂对象时,尤其是这些对象由多个部分组成,并且构建过程可按步骤进行。
    • 例子:构建一个具有多种配置的房屋,建造者类按步骤创建房屋的不同部分。

结构型模式

结构型模式关注类与对象的组合,简化类的设计和对象之间的关系。

适配器模式(Adapter)

  • 定义:将一个类的接口转换成客户希望的另一个接口。适配器使得原本由于接口不兼容而不能一起工作的类可以一起工作。

  • 场景:当现有的类接口与需要的接口不匹配时。

  • 例子:电源适配器,将不同的电压接口转换成设备所需的电压。

    Spring MVC 中的 HandlerAdapter 使用了适配器模式,使不同的 Controller 能够通过统一的接口进行调用。

    适配器将不同类型的处理器(如 ControllerHttpRequestHandler)适配到统一的 HandlerAdapter 接口,简化了调用过程。

装饰者模式(Decorator)

  • 定义:动态地给一个对象添加一些额外的职责。装饰者模式提供了比继承更有弹性的扩展功能的方式。
  • 场景:需要在不修改原有类的情况下,给对象动态地添加功能。
  • 例子:Java IO 流中的 BufferedReaderInputStream,可以动态添加缓冲或其他功能。

代理模式(Proxy)

  • 定义:为其他对象提供一种代理以控制对这个对象的访问。

  • 场景:需要控制对象的访问、延迟加载、权限控制等。

  • 例子:远程代理(调用远程对象时,使用本地代理对象来代表它)。

    Spring AOP(面向切面编程)大量使用了代理模式来对目标对象进行增强,常见的动态代理实现包括 JDK 动态代理和 CGLIB 代理。

    在不修改目标对象的情况下,通过代理对象添加额外的功能,例如日志记录、事务管理、权限检查等。

    @Transactional
    public void someMethod() {
        // 事务处理代码由代理对象注入
    }
    

结构型模式

策略模式(Strategy)

  • 定义:定义一系列算法,将每个算法封装起来,使得它们可以互换使用。
  • 场景:需要在运行时动态选择算法时。
  • 例子:支付系统中,可以选择不同的支付方式(如信用卡、PayPal、银行转账等)。

观察者模式(Observer)

  • 定义:定义对象间的一对多依赖,当一个对象的状态发生改变时,所有依赖于它的对象都能自动收到通知。
  • 场景:对象状态变化时,自动通知所有依赖对象。
  • 例子:GUI 中的按钮和点击事件,订阅者和发布者模式。

9.单例模式的实现

1.双重延迟
public class Singleton {
    private volatile static Singleton instance;
    private Singleton(){}
    public static  Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
2.懒汉式
public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public  static  Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
            }
        return instance;
    }
}
3.延迟加锁
public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public  synchronized static  Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
            }
        return instance;
    }
}
4.饿汉式
public class Singleton {
    private static  final  Singleton instance = new Singleton();
    private Singleton(){}
    public  static  Singleton getInstance(){

        return instance;
    }
}

10.select、poll和epoll的区别

selectpollepoll 都是用于处理多路复用 I/O 的系统调用,它们允许一个线程同时监听多个 I/O 操作,但它们的实现方式和性能特性有所不同。以下是它们的主要区别

select:早期的 I/O 多路复用机制,适用于少量文件描述符,但在处理大量连接时性能较差。

poll:改进了 select 的文件描述符限制问题,适用于中等规模的连接,但性能依然受限于需要遍历所有文件描述符。

epoll:高效的 I/O 多路复用机制,适用于大规模并发连接,提供更高的性能,但仅在 Linux 上可用。

select 使用一个集合(通常是一个 fd_set)来跟踪要监视的文件描述符,select 会阻塞直到至少一个文件描述符变得可读、可写或发生异常,或者超时。

poll 使用一个数组来保存需要监控的文件描述符及其事件,遍历数组,步限制文件描述符的数量

epoll 使用事件通知机制,内核维护一个事件表,并仅将有事件的文件描述符返回给应用程序。

11.链表的形式和 应用场景

链表是一种基本的数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。根据不同的实现方式,链表有几种不同的类型,每种类型适用于不同的使用场景:

  1. 单链表(Singly Linked List)
    • 结构:每个节点包含数据和指向下一个节点的指针。
    • 优点:简单,插入和删除操作较为高效(在已知节点的情况下)。
    • 缺点:只能从头到尾单向遍历。
    • 使用场景:适用于不需要频繁反向遍历的场景,比如实现简单的数据结构(如栈和队列)。
  2. 双链表(Doubly Linked List)
    • 结构:每个节点包含数据、指向下一个节点的指针和指向前一个节点的指针。
    • 优点:可以在双向上遍历,插入和删除操作比单链表更灵活。
    • 缺点:每个节点需要额外的存储空间来保存指向前一个节点的指针。
    • 使用场景:需要双向遍历的应用,如实现双向队列(Deque)、浏览器历史记录的回溯。
  3. 循环链表(Circular Linked List)
    • 结构:在循环链表中,最后一个节点的指针指向头节点,形成一个闭环。
    • 优点:能够从任意节点开始循环遍历整个链表。
    • 缺点:需要处理循环的结束情况,可能会导致无限循环。
    • 使用场景:适用于需要循环访问链表的场景,如圆形缓冲区(环形队列)和调度算法(如Round Robin)。
  4. 双向循环链表(Doubly Circular Linked List)
    • 结构:结合了双链表和循环链表的特性,节点既有前向和后向指针,也形成一个闭环。
    • 优点:支持双向遍历并形成循环结构。
    • 缺点:实现相对复杂,需要管理两个指针的循环。
    • 使用场景:需要双向遍历和循环访问的场景,如在应用程序中实现菜单或任务调度系统。
  5. 跳表(Skip List)
    • 结构:在链表的基础上增加了多个层次的链表来跳过部分节点,从而提高查找效率。
    • 优点:在平均情况下查找、插入和删除操作时间复杂度为 O(log n)。
    • 缺点:比普通链表复杂,需额外的空间来存储多个层次的链表。
    • 使用场景:需要高效查找和插入操作的场景,如实现有序集合和数据库索引。

不同类型的链表在实际应用中根据需求的不同,提供了不同的性能特性和灵活性。选择合适的链表类型可以帮助提高程序的效率和可维护性。

12.什么是零拷贝,netty中的零拷贝

零拷贝(Zero-Copy)是一种优化技术,旨在减少数据在用户空间和内核空间之间的复制操作,从而提高数据传输的效率。零拷贝主要用于文件传输、网络通信等场景,可以显著减少 CPU 的负载和提高 I/O 性能。

传统的数据传输流程通常包括以下步骤:

  1. 从文件读取数据:操作系统将数据从磁盘读入内核空间的缓冲区。
  2. 用户空间的复制:操作系统将数据从内核缓冲区复制到用户空间的缓冲区。
  3. 网络传输:将用户空间的缓冲区数据通过网络发送到另一端。

这种过程会涉及到多次内存复制,导致性能瓶颈。零拷贝技术的目标是减少或消除这些不必要的内存复制操作。

0copy实现

  1. mmap(内存映射文件)
    • 使用 mmap 系统调用将文件映射到进程的虚拟内存空间中。这样,文件数据直接存在于内存中,无需将数据从文件系统复制到用户空间缓冲区。
    • 通过直接访问映射的内存区域,可以避免文件 I/O 操作中不必要的内存复制。
  2. sendfile
    • sendfile 系统调用允许操作系统将文件数据直接从文件描述符发送到网络套接字,而不需要将数据复制到用户空间缓冲区。
    • 这样可以减少内存复制和 CPU 的消耗,提高数据传输的效率。
  3. splice
    • splice 系统调用可以在两个文件描述符之间直接移动数据,避免了数据复制到用户空间。
    • 例如,它可以在文件描述符和套接字之间直接传输数据,适用于处理大型文件的情况。

Netty 的零拷贝

Netty 是一个高性能的网络通信框架,它在内部使用了多种零拷贝技术来提高网络传输的效率。

Netty 中的零拷贝实现

  1. ByteBuf
    • Netty 使用 ByteBuf 类来处理缓冲区。ByteBuf 支持直接缓冲区(DirectByteBuf)和堆内存缓冲区(HeapByteBuf),并提供高效的读取和写入操作。
    • 直接缓冲区使用了 ByteBuffer.allocateDirect(),避免了在用户空间和内核空间之间的数据复制。
  2. FileRegion
    • Netty 提供了 FileRegion 接口,可以将文件的数据区域直接传输到网络套接字,而不需要将数据复制到用户空间。
    • FileRegion 使用了操作系统的 sendfile 系统调用来实现零拷贝。
  3. Splice
    • 在 Linux 上,Netty 支持使用 splice 系统调用来将数据从一个文件描述符传输到另一个文件描述符,而无需数据复制到用户空间。
    • 这对于在文件系统和网络之间高效地传输数据非常有用。
  4. ByteBuffer
    • Netty 使用 Java NIO 的 ByteBuffer 来处理缓冲区。ByteBuffer 可以是直接缓冲区(ByteBuffer.allocateDirect())或堆缓冲区(ByteBuffer.allocate())。
    • 直接缓冲区的使用减少了从用户空间到内核空间的数据复制,提高了 I/O 性能。

总结

零拷贝技术通过减少数据在用户空间和内核空间之间的复制,显著提高了数据传输效率。在 Netty 中,通过使用 ByteBufFileRegionspliceByteBuffer 等技术,充分利用了零拷贝的优势,提供了高效的网络通信能力。这些技术使得 Netty 在处理大规模网络数据传输时表现出色,能够有效地减少 CPU 的负载并提高 I/O 性能。


文章作者: 索冀峰
文章链接: http://suojifeng.xyz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 索冀峰 !
  目录