当前位置: 首页 > article >正文

2023-5-2面试题学习

1、内存的可见性你了解吗,讲述一下?

        内存可见性是指多个线程访问同一共享变量时,在一个线程修改了该变量值后,下一个线程能立即看到这种变化的能力。

        如果一个变量在多个线程间共享,那么为了避免出现数据不一致的情况,线程执行时会把变量存储在各自的CPU缓存中,而不是直接从内存中获取,这个时候可能会导致可见性问题,因为一个线程修改了变量的值,但是另一个线程并不知道这个变量的值被修改了,导致数据不一致的情况。

        为了解决这个问题,在Java中提供了volatile关键字来保证内存可见性。如果一个变量被声明为volatile,那么每次访问这个变量都会直接从内存中读取,并且每次修改也会立刻写入到内存中。这确保了所有的线程都能看到变量的最新值,从而避免出现内存可见性问题。

        也可以进行加锁synchronized或者lock锁解决这个问题。

2、 讲一下面向对象的三大特性,并说出自己理解

        面向对象的三大特性是:继承、封装和多态。

        封装:是把成员方法和成员变量都放在一个类中,其目的是隐藏内部的细节,防止外部干扰和误操作,提高代码的安全性。

        继承:指的是通过继承已经存在的类所拥有的成员变量和方法,而形成新的类。可以提高代码的复用性。

        多态:指的是对同一种操作(也可以说是方法),不同对象可以进行不同的行为。

3、C++多态的原理

        C++中的多态有两种方式:运行时多态和编译时多态。

        运行时多态是通过虚函数来实现的,当一个类定义了虚函数,它的子类如果没有重写该虚函数,则默认使用父类的实现,当父类指针指向子类对象的时候,

Animal a = new Dog();

 调用该虚函数会按照子类实现来执行,从而实现多态特性。

虚函数表是一个类的静态成员,它存储了该类所有虚函数的地址。当一个类被实例化时,会
在其对象中生成一个虚函数指针指向该类的虚函数表,这样就可以在运行时动态地确定调用的虚函数。

举个例子,假设有一个基类Animal和它的两个派生类Dog和Cat,其中Animal定义了一个虚函数makeSound()
,而Dog和Cat分别实现了该虚函数。那么,在编译时,编译器会为Animal、Dog和Cat中的虚函数makeSound()
生成一个相应的虚函数表,并将其存储在静态内存中。

当创建一个Animal类型的对象时,会在对象中生成一个虚函数指针vptr,该指针指向Animal的虚函数表。
同样地,当创建一个Dog类型或Cat类型的对象时,也会在对象中生成一个虚函数指针vptr,只不过该指
针分别指向Dog和Cat的虚函数表。

当程序调用某个对象的虚函数makeSound()时,实际上是通过该对象的虚函数指针vptr去访问该对象所属类的
虚函数表,从而找到该虚函数的地址,最终调用该虚函数。这种方式可以在程序运行时动态地确定调用的函
数,实现了多态性。

总结一下,虚函数表的工作原理可以简单归纳为:在程序运行时动态确定虚函数的地址,并通过该地址调用
正确的虚函数,实现多态性。

4 进程和线程的区别是什么?

        我们将计算机系统看成一个工厂,那么CPU就是工人,以单核CPU为例,那么这个工厂只有一个工人。

        进程就是这个工厂的产品生成线,我们可以有产品A线,B线等等。

        线程就是产品线上的几道工序。

那么进程要工作的时候,需要有工人(也就是CPU)来处理工序(线程),同时只有工序是不够的,肯定需要有原料等输入,然后对原料进行加工。

        这也就解释了进程是资源分配和调度的基本单位,而线程是CPU执行的基本单位。而且线程在进程上执行,那么多个线程就共享进程的资源。

5 记录对字节跳动面试的学习

字节跳动后端面经分享——从一面到HR面 - 知乎 (zhihu.com)

  1. 自我介绍

省略1分钟...

select、poll、epoll?

        深入浅出理解select、poll、epoll的实现 - 知乎 (zhihu.com)

        这三个都是多路复用方面的技术。多路复用指的是:复用一个线程处理多个socket。

      1.1 select 

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

        select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。当用户系统调用select时,select会将需要监控的readfds集合拷贝到内核空间(假设监控的仅仅是socket读),然后遍历自己监控的socket sk。如果发现某些sk是可以读,然后将可读socket的个数返回给用户。

poll和select很类似,都需要将监控集合拷贝到内核空间,造成性能问题。

        1.2 epoll

        相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率

epoll采用三个函数:epoll_create,epoll_ctl,epoll_wait,来做上述select的事情,监控文件描述符,当文件描述符有事件时,向用户返回事件。

  • epoll_create:创建一个epoll结构体(红黑树+链表),返回其句柄
  • epoll_ctl:向 epoll 对象中添加/修改/删除要管理的连接
  • epoll_wait:等待其管理的连接上的 IO 事件

epoll_create:创建一个红黑树,和一个就绪描述符链表。

epoll_ctl:epoll的事件注册函数,它不同于select,是想红黑树上面祖册要监听的事件类型。

epoll_wait:等待事件的产生,检查就绪描述符链表,是否为空不为空则返回就绪链表中FD的个数

        1.3 epoll的两种触发模式?

  • level 模式:该模式就是只要还有没有处理的事件就会一直通知
  • edge 模式:该模式是当状态发生变化时才会通知

       2 TCP三次握手过程,有什么状态,状态机如何变化?

        客户端从:closed --> syn_sent-->established

        服务器从:listen --> syn_revd-->established

四次挥手:

客户端:established-->final_wait-->time_wait-->closed

服务端:established-->close_wait-->last_ack-->closed

        3 什么是 TIME_WAIT 状态,为什么需要 TIME_WAIT 状态?时间是多久?

        2MSL时间是从客户端接收到FIN后发送ACK开始计时的。如果在这个时间段内,服务器没有收到ACK应答报文段,会重发FIN报文段,如果客户端收到了FIN报文段,那么2MSL的时间将会被重置。如果在2MSL时间段内,没有收到任何数据报,客户端则会进入CLOSE状态。

        4 Linux 中一个进程的虚拟内存分布长什么样?

         只读数据段(rodata):const修饰的全局变量是存放在常量段的

         代码段:存放代码

        数据段:存放全局变量和静态变量(已经初始化和未初始化的)

        堆:动态内存的分配

        内存映射段:常被用来加载共享库(动态库)

        栈:存放函数中局部变量

5 为什么要用虚拟内存?

  • 将主存当作辅存的高速缓存,经常活动的东西放在主存中,就像 GTA5 几十 GB 大的东西都放主存中是放不下的,因此可以高效利用主存
  • 每个进程地址空间都一样,方便管理
  • 进程间的隔离,避免进程破坏其他进程的地址空间

        

 6 虚拟地址映射为物理地址的过程?

       cpu获得虚拟内存,然后交给MMU(一般是通过页表进行转换)进行地址翻译成真实的物理内存。 TLB与MMU_mmu tlb_wagsyang的博客-CSDN博客

 

7 使用线程有哪些好处与坏处?

        好处:上下文切换代价小,通信方便       

        坏处:注意死锁,注意对共享资源的访问。

8 进程有哪些同步的机制?

        临界区、互斥、信号量、事件

9 什么是稳定排序?

        利用关键词排序后,关键词相同的元素之间的相互顺序不变的排序算法

10 手撕

        189.数组循环右移。将一个长度为 n 的数组,循环右移 k 位,要求时间复杂度为 O(n) 空间复杂度为 O(1) 。

思路:先整体reverse,然后前k位reverse,然后后n-k位reverse

class Solution {
    public void rotate(int[] nums, int k) {
        k %= nums.length;
        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.length - 1);
    }

    public void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start += 1;
            end -= 1;
        }
    }
}


http://www.kler.cn/a/16497.html

相关文章:

  • 【最新版】Stable Diffusion4.9(AI绘画)下载及安装教程(附软件安装包)!
  • 解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url
  • 24.11.13 Javascript3
  • unity基础,点乘叉乘。
  • 【C++】类与对象的基础概念
  • 【JAVA】正则表达式中的中括弧
  • 746. 使用最小花费爬楼梯
  • Cell:癌症研究的下一个问题是什么?
  • 大学生学java编程的就业前景怎么样?我来聊聊自己的见解
  • 通过Python的PIL库给图片添加文本水印
  • 【网络协议详解】——GNS3的使用(学习笔记)
  • 计算机网络笔记:TCP协议 和UDP协议(传输层)
  • ChatGPT调教指南(中文)
  • 回到大学时光,我想对当时的自己说些什么
  • DDD系列:四、领域层设计规范
  • 存储资源调优技术——SmartThin智能精简配置技术
  • C++动态规划模板汇总大全
  • STM32物联网实战开发(4)——基本定时器
  • 32k*16 薪,3年自动化测试历经3轮面试成功拿下华为Offer....
  • 【Java笔试强训 7】
  • GEE:MODIS计算遥感指数(NDVI、BSI、NDSI、EVI、LSWI、SIPI、EBI等)
  • 吉布斯采样方法
  • 设计模式-单例模式
  • 一文搞懂PMP挣值管理那些让你头疼的公式
  • mockjs学习笔记
  • maven中的 type ,scope的作用