目录

  • 高频考点
    • 操作系统篇
      • 1.进程与线程的区别【常问】
      • 2.进程的通信方式?【常问】
      • 3.操作系统调度方法?【腾讯】
      • 4.缓存算法(页面置换算法)?【字节、腾讯】
      • 5.什么是死锁?如何避免死锁?
      • 6.IO模型
      • 7.IO复用:select、epoll、poll的区别
      • 8.Linux常用命令【腾讯ping、iotop】
      • 9.虚拟地址怎么映射到物理地址【字节】
    • 计算机网络篇
      • 1.网络协议分层【腾讯七层】
      • 2.HTTP请求结构?【字节】
      • 3.HTTP/TCP三次握手和四次挥手【字节、腾讯】
      • 4.客户端通过HTTPS与Web服务器通信过程
      • 5.TCP拥塞控制?【腾讯】
      • 6.DNS域名解析【字节】
    • Java基础篇
      • 1.Spring的aop和ioc【腾讯】
      • 2.JDK1.8的改进?【阿里】
      • 3.Spring Bean生命周期?【腾讯】
      • 4.final关键字主要用在哪些地方?【阿里】
      • 5.hashCode()与equals()?
      • 6.Java线程
      • 7.锁——Synchronized
      • 8.锁——ReentrantLock【阿里】
      • 9.锁——CAS【阿里】
      • 10.锁——Volatile关键字
      • 11.Hashmap和hashtable的区别?
      • 12.ConcurrentHashMap是怎么实现的?
      • 13.hashmap的扩容说一下,怎么实现的?【腾讯】
      • 14.Java类加载器
      • 15.JVM详解以及堆和栈的区别【腾讯、字节】
      • 16.Java垃圾回收?CMS和G1的区别?【腾讯、字节】
      • 17.重载和重写?【阿里】
      • 18.Java基本类型?【阿里】
      • 19.Java设计模式(23种)中常见的几种?【阿里】
      • 20.线程池
      • 21.Java异常的类型?【阿里】
      • 22.红黑树?【腾讯】
    • Mysql篇
      • 1.索引实现底层?为什么不用B树?B+树好在哪?【腾讯】
      • 2.索引最左匹配原则知道吗,为什么?【字节】
      • 3.mysql 索引使用时注意什么?索引失效原因?【字节】
      • 4.数据库中的四种事务隔离级别【腾讯】
      • 5.Mysql存储引擎Innodb和MyISAM的区别是什么?【腾讯】
      • 6.什么是慢查询?如何处理慢查询?【字节】
      • 7.简述数据库的事务和ACID【网易】
    • 手撕代码篇
    • 其它
  • 面试经验总结

高频考点

操作系统篇

操作系统比较常考察的就是进程和线程、一些调度算法、死锁和IO模型。另外就是一些零碎的问题,我将一一介绍。

1.进程与线程的区别【常问】

进程和线程都是CPU工作时间段的描述,不过是颗粒大小不同。一个线程只能属于一个进程,但是一个进程可以拥有多个线程。进程是资源分配的最小单位,线程是程序执行的最小单位。进程有自己的独立地址空间,而线程没有。切换线程、创建线程、线程通信的开销均小于进程。

2.进程的通信方式?【常问】

(1)管道:它是半双工的(单向的),具有固定的读端和写端。管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。在无名管道中,进程只能访问自己或祖先创建的管道。而命名管道有名字,该名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。
(2)消息队列:消息队列就是一个消息的链表,是一系列保存在内核中消息的列表。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
(3)共享内存:共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存写入和读出,从而实现进程间的通信。采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。进程之间在共享内存时,不是读写少量数据后就解除映射,有新的通信时再重新建立共享内存区域;而是保持共享区域,直到通信完毕为止,这样数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件,因此,采用共享内存的通信方式效率非常高。

3.操作系统调度方法?【腾讯】

(1)先来先服务:作业调度中,每次从后备作业队列中选择最先进入该队列的作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。进程调度中,每次从就绪队列中选择最先进入该队列的进程,将处理机分配给它,使之投入运行,直到完成或因某种原因而阻塞时才释放处理机。FCFS调度算法的特点是算法简单,但效率低;对长作业比较有利,但对短作业不利。
(2)短作业(进程)优先:短作业优先(SJF)调度算法是从后备队列中选择运行时间最短的作业,将它们调入内存运行。短进程优先(SPF)调度算法,则是从就绪队列中选择估计运行时间最短的进程,将处理机分配给它,使之立即执行,直到完成或发生某事件而阻塞时才释放处理机。SJF调度算法的平均等待时间、平均周转时间最少,但对长作业很不利。
(3)优先权调度:在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最高的作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。
优先级用于描述作业运行的紧迫程度。根据新的更高优先级进程能否抢占正在执行的进程,可将该调度算法分为:
非剥夺式优先级调度算法。当某一个进程正在处理机上运行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于其自身的原因而主动让出处理机时(任务完成或等待事件),才把处理机分配给更为重要或紧迫的进程。
剥夺式优先级调度算法。当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。
而根据进程创建后其优先级是否可以改变,可以将进程优先级分为以下两种:
静态优先级。优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求。
动态优先级。在进程运行过程中,根据进程情况的变化动态调整优先级。动态调整优先级的主要依据为进程占有CPU时间的长短、就绪进程等待CPU时间的长短。
(4)高响应比优先:主要用于作业调度,每次先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。响应比=(等待时间+要求服务时间)/要求服务时间。当作业的等待时间相同时,则要求服务时间越短,其响应比越高,有利于短作业。当要求服务时间相同时,作业的响应比由其等待时间决定,等待时间越长,其响应比越高,因而它实现的是先来先服务。
(5)基于时间片的轮转调度:系统将所有就绪进程按到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中第一个进程执行,即先来先服务,但仅能运行一个时间片,如100ms。在使用完一个时间片后,即使进程并未完成其运行,它也必须释放出处理机给下一个就绪的进程,而被剥夺的进程返回到就绪队列的末尾重新排队,等候再次运行。
时间片的大小对系统性能的影响很大。如果时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度就退化为先来先服务调度。如果时间片很小,那么处理机将在进程间过于频繁切换,使处理机的开销增大。因此时间片的大小应选择适当。
(6)多级反馈队列调度:进程在进入待调度的队列等待时,首先进入优先级最高的Q1等待。首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程。对于同一个队列中的各个进程,按照FCFS分配时间片调度。比如Q1队列的时间片为N(优先级越高,时间片越短),若Q1中的作业在经历了时间片N后若还没有完成,则进入Q2队列等待,若Q2的时间片用完后作业还不能完成,一直进入下一级队列末尾,直至完成。在最后一个队列QN中的各个进程,按照时间片轮转分配时间片调度。在低优先级的队列中的进程在运行时,又有新到达的作业,此时须立即把正在运行的进程放回当前队列的队尾,然后把处理机分给高优先级进程。任何时刻,只有当第1~i-1队列全部为空时,才会去执行第i队列的进程(抢占式)。当再度运行到当前队列的该进程时,仅分配上次还未完成的时间片,不再分配该队列对应的完整时间片。

4.缓存算法(页面置换算法)?【字节、腾讯】

(1)FIFO先进先出:如果一个数据最先进入缓存中,则应该最早淘汰掉。当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。
(2)LFU(Least Frequently Used)最近最少使用算法:如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小,当某一数据项被命中时,访问频次自增,在淘汰的时候淘汰访问频次最少的数据。
(3)LRU(Least Recently Used)最近最久未使用算法:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

5.什么是死锁?如何避免死锁?

死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
死锁的产生条件
(1)互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用
(2)请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放
(3)不可抢占条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,只能自己来释放(4)循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路,造成永久阻塞。
预防死锁的发生
(1)破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
(2)破坏“循环等待”条件:将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的资源才能申请较大编号的资源。

6.IO模型

(1)同步阻塞IO(Blocking IO):用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作。而用户需要等待read将数据读取到后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,对CPU的资源利用率不够。
(2)同步非阻塞IO(Non-blocking IO):用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地调用read发起IO请求,直到数据到达后,才真正读取到数据,继续执行。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。
(3)IO多路复用(IO Multiplexing):使用Reactor模式实现,Reactor类可用于IO事件处理,并使用handle_events实现事件循环,不断调用select函数,通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程为感兴趣的socket注册事件处理器之后可以继续做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),进行数据读取、处理的工作。用户可以注册多个socket,同时处理多个socket的IO请求。用户可以,然后不断地调用select读取被激活的socket。虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的。
(4)异步IO(Asynchronous IO):使用Proactor模式实现,用户使用AsynchronousOperationProcessor(异步操作进程)提供的API发出IO请求,然后继续执行其他任务,当异步IO完成后,内核将数据发送到Proactor,Proactor将IO完成的信息通知给用户线程,完成异步IO。

7.IO复用:select、epoll、poll的区别

(1)Select:时间复杂度O(n),仅知道有I/O事件发生,却并不知道是谁,只能无差别轮询所有流,找出能读出数据或者写入数据的流,对他们进行操作。同时处理的流越多,无差别轮询时间就越长。缺点是单个进程的监视端口大小有限,每次调用select,都需要把fd(文件描述符)集合从用户态拷贝到内核态,然后进行轮询,这个开销在fd很多时会很大,需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
(2)Poll:时间复杂度O(n),poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构(链表结构)而不是select的fd_set结构,因此它没有最大连接数的限制。poll还有一个特点是“水平触发”,如果报告了fd后没有被处理,那么下次poll时会再次报告该fd。
(3)Epoll:时间复杂度O(1),epoll实际上是事件驱动的,会把哪个流发生了怎样的I/O事件通知我们。epoll有EPOLL LT和EPOLL ET两种触发模式。LT模式下,只要这个fd还有数据可读,每次epoll_wait都会返回它的事件提醒用户程序去操作,而在ET模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或遇到EAGAIN错误。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

8.Linux常用命令【腾讯ping、iotop】

(1)ping IP地址用来检测网络的连通情况和分析网络速度,bytes值表示数据包大小,time值表示响应时间,这个时间越小,说明这个地址速度越快,TTL值该数据报被保存的时间;ping -a可以将地址解析为主机名;ping -r count记录传出和返回数据包经过的路由,最多九个。
(2)top命令经常用来监控linux的系统状况,是常用的性能分析工具,能够实时显示系统中各个进程的资源占用情况。可以显示当前登录用户数,进程总数,正在运行进程数,睡眠、停止的进程数,CPU信息等。
(3)kill 信号参数 进程PID -1代表SIGHUP信号,作用类似重新启动进程;-2代表SIGINT信号,作用相当于在命令行输入Ctrl+C组合键中断进程运行;-9代表SIGKILL信号,代表强制中断进程;-15代表SIGTERM信号,表示正常的终止进程;-17代表SIGSTOP信号,相当于在终端输入Ctrl+Z组合键来暂停进程的运行。

9.虚拟地址怎么映射到物理地址【字节】

CPU通过地址来访问内存中的单元,CPU向MMU(内存管理单元)发送虚拟地址,MMU将虚拟地址映射成物理地址发到内存芯片的引脚上。不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
Win32通过一个两层的表结构来实现地址映射,第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页被分为1024项,每一项大小为4字节,称为页目录项。第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页也被分为1024项,每一项大小为4字节,被称为页表项,可知共有1024×1024个页表项。每一个页表项对应一个物理内存中的某一个“内存页”,即共有1024×1024个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。
每个虚拟地址都是一个32位的整数值,也就是我们平时所说的指针,即指针的大小为4B。它由三部分组成:第一部分,即前10位为页目录下标,用来寻址页目录项。第二部分则用来在页表内寻址,用来找到页表项。第三部分用来在物理内存页中找到对应的字节,一个页的大小是4KB,12位刚好可以满足寻址要求。由于页目录项和页表项大小为4KB,所以第一和第二部分的地址需要左移二位。
PS:我是这么回答的,但面试官好像不太满意?不太清楚他想问的是不是这个

计算机网络篇

1.网络协议分层【腾讯七层】

物理层:定义物理设备之间如何传输数据
数据链路层:在通信的实体间建立数据链路链接
网络层:为数据在节点之间的传输创建逻辑链路
传输层:向用户提供可靠的端到端服务,传输层通过封装向高层屏蔽了下层数据通信的细节
【会话层、表示层】
应用层:为应用软件提供了很多服务,构建于tcp协议之上,屏蔽了网络传输相关的细节

2.HTTP请求结构?【字节】

HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据;请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说 Content-Length必须出现。空行的作用是告诉服务器请求头部到此为止。请求数据:若方法字段是GET,则此项为空,没有数据,若方法字段是POST,则通常来说此处放置的就是要提交的数据。
HTTP响应报文也由三部分组成:响应行、响应头、响应体;响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK。响应头用于描述服务器的基本信息以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。

3.HTTP/TCP三次握手和四次挥手【字节、腾讯】

三次握手时序图

第一次握手:主机A发送位码为SYN=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),SYN=1,ack=1,随机产生seq=7654321的包;
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。

为什么要采用三次握手
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。比如client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段,但是server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求,于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了,由于client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据,但server却以为新的运输连接已经建立,并一直等待client发来数据。所以没有采用“三次握手”,这种情况下server的很多资源就白白浪费掉了。

对服务器进行攻击怎么防范
黑客仿造IP大量的向server发送TCP连接请求报文包,但不回复第三次的报文,从而使得server拒绝其他正常的连接请求。
缩短服务器接收客户端SYN报文之后的等待连接时间,也就是server接收到SYN报文段,到最后放弃此连接请求的超时时间。SYN proxy代理,作为server与client连接的代理,代替server与client建立三次握手的连接,SYN proxy与client建立好了三次握手连接之后,确保是正常的TCP连接,而不是TCP泛洪攻击,那么SYN proxy就与server建立三次握手连接,作为代理来连通client与server。

四次挥手时序图

第一次挥手:客户端发送一个FIN(结束)和seq随机数,用来关闭客户到服务端的连接。
第二次挥手:服务端收到这个FIN,他发回一个ACK(确认),ack number为收到seq+1。服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器可以继续发送未发送完的数据,客户端依然要接受。
第三次挥手:服务端发送一个FIN(结束)和随机数seq到客户端,服务端关闭客户端的连接。
第四次挥手:客户端发送ACK(确认)报文确认,ack number为收到seq+1,这样关闭完成。

为什么是4次挥手呢
当client发出FIN报文段时,只是表示client已经没有数据要发送了,client告诉server,它的数据已经全部发送完毕了;但是,这个时候client还是可以接受来server的数据;当server返回ACK报文段时,表示它已经知道client没有数据发送了,但是server还是可以发送数据到client的;当server也发送了FIN报文段时,这个时候就表示server也没有数据要发送了,就会告诉client,我也没有数据要发送了,如果收到client确认报文段,之后彼此就会愉快的中断这次TCP连接。

为什么客户端最后还要等待2MSL
第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且重启2MSL计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

4.客户端通过HTTPS与Web服务器通信过程

一个HTTPS请求实际上包含了两次HTTP传输,具体如下:
1.客户端向服务器发起HTTPS请求,连接到服务器的443端口
2.服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
3.服务器将自己的公钥发送给客户端。
4.客户端收到服务器端的证书之后,会对证书进行检查,验证其合法性,如果发现发现证书有问题,那么HTTPS传输就无法继续。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为客户端密钥。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
5.客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
6.服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
7.然后服务器将加密后的密文发送给客户端。
8.客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。

5.TCP拥塞控制?【腾讯】

(1)慢启动:TCP应用在启动一个连接时会向网络中发送大量的数据包,这样很容易导致路由器缓存空间耗尽,网络发生拥塞。由于TCP源端一开始并不知道网络资源当前的利用状况,因此新建立的TCP连接不能一开始就发送大量数据,而只能逐步增加每次发送的数据量。当建立新的TCP连接时,拥塞窗口(cwnd)初始化为一个数据包大小。源端按cwnd大小发送数据,每收到一个ACK确认,cwnd就增加一个数据包发送量。
(2)拥塞避免:由此可见慢启动的增长速度很快,cwnd也会指数增长,因此设定一个慢启动阈值ssthresh,当cwnd<ssthresh的时候,tcp处于慢启动状态,如果cwnd达到慢启动阀值,则试探性的发送一个segment,如果server超时未响应,TCP认为网络能力下降,则重设慢启动阀值ssthresh=当前cwnd/2,并且立刻把cwnd设为1,恢复到慢启动状态。
(3)快速重传:当接收端收到比期望序号大的报文段时,便会重复发送最近一次确认的报文段的确认信号,称之为冗余ACK。如果在超时重传定时器溢出之前,接收到连续的三个重复冗余ACK(其实是收到4个同样的ACK,第一个是正常的,后三个才是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出,大大提高了效率。
(4)快速恢复:在拥塞避免中,一旦出现超时重传,TCP 就会把ssthresh的值设置为cwnd值的一半,同时 cwnd 设置成 1。但在快速恢复中,一旦出现超时重传,或者收到第三个重复的 ack 时(快重传),TCP 会把慢启动门限 ssthresh 的值设置为 cwnd 值的一半,同时 cwnd = ssthresh (在有些版本中,会让 cwnd = ssthresh + 3)。

6.DNS域名解析【字节】

DNS域名服务器可分为根域名服务器、顶级域名服务器(TLD服务器)、权限域名服务器和本地域名服务器(local name server)。
根域名服务器是最高层次的域名服务器。根域名服务器管理下一层的顶级域名服务器的域名和IP地址。现在全世界的多个地点都有根域名服务器机器,这样大部分DNS域名服务器都能就近找到一个根域名服务器。顶级域名服务器负责管理在其上注册的所有二级域名。权限域名服务器负责一个区的域名服务器,如果一个权限域名服务器对其接收到的DNS查询请求不能给出最终的查询结果时,就会告诉发出请求的DNS客户,下一步应该去找哪个权限域名服务器。当一个主机发出DNS查询请求时,首先就发送给本地域名服务器,这个本地域名服务器通常是你本地互联网接入的一个DNS解析服务,这个域名服务器通常离你也不会太远,只有几个路由的距离。如果需要查询的主机和发出请求的主机同属一个本地域名服务器,那么会很快给出解析结果,不需要再去询问其他域名服务器。
域名解析过程:浏览器检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,则解析过程结束。域名缓存时间可以通过TTL属性设置;如果用户浏览器缓存未命中,浏览器会在操作系统缓存中查找是否有这个域名对应的DNS解析结果。操作系统的域名解析在window中通过hosts文件(只读,防止被篡改)来设置,你可以将任何域名解析到能访问的IP地址;如果到这两步还没有完成域名解析,就会请求真正的域名服务器来解析这个域名了。
本机将DNS域名解析请求发送给本地域名服务器,本地域名服务器一般都会缓存域名解析结果,当然这个缓存时间是受域名失效时间控制的。大部分域名解析到这里都已经完成了,所以本地域名服务器承担了大部分的域名解析工作。如果本地域名服务器没有命中,直接到根域名服务器请求解析。根域名服务器返回给本地域名服务器一个顶级域名服务器,这里可以理解为本地域名服务器代理本机进行域名解析查询,本地域名服务器就是一个DNS客户。顶级域名服务器查找并返回该域名对应的权限域名服务器的地址,这个权限域名服务器就是你注册的域名服务器。权限域名服务器会查询存储的域名和IP地址映射关系,连同一个TTL值返回给本地域名服务器。本地域名服务器获得该域名对应的IP地址和TTL值,并缓存这个域名和IP地址的映射关系,缓存时间由TTL值控制。本地域名服务器把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中。

Java基础篇

Java问的真的很多了,主要的知识点是线程、锁、哈希表、JVM、垃圾回收,另外还有其他一些零碎的问题。

1.Spring的aop和ioc【腾讯】

AOP面向切面编程。主要包含了通知、切点和连接点等术语:通知定义了切面是什么以及何时调用,切点定义了何处,切点的定义会匹配通知所要植入的一个或多个连接点,通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。
IoC是Spring框架的核心内容,可以使用XML配置,Java配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
通过注解的方式,把对象加入ioc容器,创建对象以及处理对象依赖关系,相关的注解:@Component 指定把一个对象加入IOC容器,@Repository 作用同@Component,在持久层使用;@Service作用同@Component,在业务逻辑层使用;@Controller作用同@Component,在控制层使用;@Resource 属性注入。当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean, 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据,如果查询的结果不止一个,那么@Autowired会根据名称来查找,如果查询的结果为空,那么会抛出异常。解决方法时,使用required=false。
@Autowired和@Resource之间的区别?
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;@Resource默认是按照名称来装配注入的。@Resource注解是由J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用@Resource的方式。

2.JDK1.8的改进?【阿里】

Lambda表达式是对匿名内部类的简写,匿名内部类特点:基于多态(多数基于接口编程),实现类无需名称,允许多个抽象方法。而Lambda表达式要求接口中有且仅有一个抽象方法,而且必须具有上下文推断,也就是方法的参数或局部变量类型必须为Lambda对应的接口类型。
StreamAPI:流是数据渠道,是一个来自数据源的元素队列,用于操作数据源(集合、数组等)生成的元素序列。Stream自己不会存储元素,而是按需计算。Stream不会改变源对象,并且能返回一个持有结果的新流。Stream操作是延迟操作,意味着他们会等到需要结果的时候才执行。Stream操作的三个步骤:创建Stream:一个数据源(如集合、数组),获取一个流,所有的 Collection 集合都可以通过stream默认方法获取流;Stream接口的静态方法of可以获取数组对应的流;中间操作:一个操作链,对数据源的数据进行处理,forEach逐一处理,filter条件过滤生成另一个子集流,map将元素转换成其他形式或提取信息,接受一个函数作为参数,reduce将流中元素反复结合起来得到一个值;终止操作:一个终止操作,执行中间操作链并产生结果。

3.Spring Bean生命周期?【腾讯】

实例化、属性赋值、初始化、销毁。主要逻辑都在doCreate()方法中,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应。createBeanInstance()实例化,populateBean()属性赋值,initializeBean()初始化。

4.final关键字主要用在哪些地方?【阿里】

final定义成员:在类中定义变量时在前面加上final关键字,代表这个变量一旦被初始化便不可改变,对基本类型来说是值不可变,对于对象变量来说其引用不可变。对于成员变量的初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一。
final定义方法:将方法声明为final,则不允许任何从此类继承的类来覆写这个方法,但是仍然可以继承这个方法。
final定义类:final类无法被任何类继承。对于final类中的成员,可以定义其为final,也可以不是final。而类中的方法就成了final型。

5.hashCode()与equals()?

equals()方法是用来判断其他的对象是否和该对象相等,比较的是两个对象的地址值。但String 、Math、Integer、Double等这些封装类在使用equals()方法时覆盖了object类的equals()方法,比较的是内容。hashCode()是根据对象的地址或内容算出来的int数值。当equals()方法被override时,hashCode()也要被override。按照一般hashCode()方法的实现来说,相等的对象,它们的hash code一定相等。
所有对于需要大量并且快速的对比,如果都用equal()去做显然效率太低,解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等,也就不必再用equal()去对比了,如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的正确性。

6.Java线程

线程的状态及转换
初始(NEW):新创建了一个线程对象,但还没有调用start()方法;
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
阻塞(BLOCKED):线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态;
等待(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态;
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自动被唤醒;
终止(TERMINATED):表示该线程已经执行完毕。

Sleep()、wait()和yield()的区别?Thread.sleep(0)什么意思
当前线程调用sleep()方法,当前线程进入超时等待状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态,加入同步队列,给其它线程执行的机会。当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。当前线程调用yield()方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程,yield() 方法只会给相同优先级或更高优先级的线程运行的机会。
sleep(0)触发操作系统立刻重新计算各个线程的优先级,重新进行一次CPU竞争。

notify和notifyAll,说说具体使用的场景
notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。在生产者消费者模型中,通常使用notifyAll()。

7.锁——Synchronized

使用方法
修饰一个方法:public synchronized void method(){},synchronized关键字不能继承,必须显式地在子类的这个方法中加上synchronized关键字才可以,还可以在子类方法中调用父类中相应的方法,定义接口方法时不能使用synchronized关键字,构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步;
修饰一个代码块:public void method(){synchronized(this) {}},一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞;
修饰静态方法:public synchronized static void method() {},锁定的是这个类的所有对象;
修饰一个类:class ClassName {public void method() {synchronized(ClassName.class) {}}},给这个类T加锁,类中的静态方法和静态变量在内存中都只有一份。所以一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把类锁。

Synchronized实现同步原理
代码块实现同步原理:在Java中所有的对象,都会有一把内置锁,也称作监视器锁(monitor,也就是synchronized(this) {}里的this),这是种排他锁。当monitor被占用时就会处于锁定状态,线程执行monitor enter指令时尝试获取monitor的所有权,过程如下:1)如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;2)如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;3)如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。执行monitor exit的线程必须是对象所对应的monitor的所有者,指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。synchronized关键字将代码块中代码由并行转变成了串行,这样就保证了代码被顺序执行。
方法实现同步原理:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

监视器(monitor)锁是怎么实现的?
Monitor其实是一种同步工具,也可以说是一种同步机制,它通常被描述为一个对象。Monitor中包含_owner指向持有Monitor对象的线程,_WaitSet存放处于wait状态的线程队列,_EntryList存放处于等待锁block状态的线程队列,_recursions表示锁的重入次数,_count用来记录该线程获取锁的次数。
当多个线程同时访问一段同步代码时,首先会进入_EntryList队列中,当某个线程获取到对象的monitor后进入_Owner区域并把monitor中的_owner变量设置为当前线程,同时monitor中的计数器_count加1。即获得对象锁。若持有monitor的线程调用wait()方法,将释放当前持有的monitor,_owner变量恢复为null,_count自减1,同时该线程进入_WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。

8.锁——ReentrantLock【阿里】

AQS的原理
队列同步器AQS是用来构建锁或其他同步组件的基础框架,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock)。内部使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,其中内部状态state,等待队列的头节点head和尾节点head,都是通过volatile修饰,保证了多线程之间的可见。头节点指向当前持有锁的线程,当线程竞争失败,则被插入队尾。每个节点中有一个waitStatus变量,用于描述节点当前的状态。只有前一个节点的状态为SIGNAL时,当前节点的线程才能被挂起。子类重写tryAcquire和tryRelease方法通过CAS指令修改状态变量state。

ReentrantLock如何实现的可重入
重入锁,即线程可以重复获取已经持有的锁而不产生死锁。在非公平和公平锁中,都对重入锁进行了实现。如果申请锁的进程就是当前持有锁的进程,则令AQS中的state加一。当state=0则释放锁。

ReentrantLock与synchronized的比较
相同:ReentrantLock提供了synchronized类似的功能和内存语义。
不同:(1)ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票(2)ReentrantLock提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合(3)ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些(4)ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放(5)ReentrantLock支持中断处理,且性能较synchronized会好些。

ReentrantLock中的Condition
Condition是一个接口,用await()和signal()方法来替代传统的wait()、notify()实现线程间的协作,一个Lock对象里可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,在调度线程上更加灵活。而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在这个对象上。

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用。

9.锁——CAS【阿里】

CAS(比较并交换)有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。即CPU去更新一个值,但如果想改的值不再是原来的值,操作就失败。CAS是CPU指令级的操作,只有一步原子操作,所以非常快。但会存在cache miss cas问题影响开销。

CAS存在的ABA问题,怎么解决
两个并发的CAS操作,后者的置换结果覆盖了前者的置换结果,导致最终的结果出错。提供了一个类AtomicStampedReference,引入时间戳来解决ABA问题。首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

10.锁——Volatile关键字

volatile可看作轻量级的synchronized。和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量,在声明一个可能被多线程同时访问的变量时,使用volatile修饰就可以了,无法修饰方法及代码块。
为了提高处理器的执行速度,在处理器和内存之间增加了多级缓存来提升。但是由于引入了多级缓存,就存在缓存数据不一致问题。缓存一致性协议保证了各个处理器的缓存是一致的。
缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
如果一个变量被volatile所修饰的话,在每次进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile在并发编程中,其值在多个缓存中是可见的。(可见性)
有序性即程序执行的顺序按照代码的先后顺序执行。由于处理器优化和指令重排等。volatile禁止指令重排优化从而保证了有序性
内存屏障:volatile通过内存屏障实现了防止指令重排的目的。同时lock前缀指令相当于一个内存屏障,它会告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值。
Java内存屏障主要有Load和Store两类:对读屏障(Load Barrier)来说,在读指令前插入读屏障,可以让高速缓存中的数据失效,重新从主内存加载数据。对写屏障(Store Barrier)来说,在写指令之后插入写屏障,能让写入缓存的最新数据写回到主内存。
在每个volatile写操作的前面插入一个StoreStore屏障。
在每个volatile写操作的后面插入一个StoreLoad屏障。
在每个volatile读操作的前面插入一个LoadLoad屏障。
在每个volatile读操作的后面插入一个LoadStore屏障。
LoadLoad:对于语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。

11.Hashmap和hashtable的区别?

HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

12.ConcurrentHashMap是怎么实现的?

ConcurrentHashMap是线程安全且高效的HashMap,在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的HashTable效率又非常低下,于是便有了ConcurrentHashMap。
在JDK1.7中,ConcurrentHashMap使用锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
在JDK1.8中,采用Node + CAS + Synchronized来保证并发安全进行实现。取消了segment,结构与hashmap相同。仅在发生hash冲突时上锁。
PS:这个展开来说很长,建议自己去查资料看看

13.hashmap的扩容说一下,怎么实现的?【腾讯】

当HashMap决定扩容时,会调用HashMap类中的resize(int newCapacity)方法,参数是新的table长度,新的table长度是原table长度的2倍。然后将原table中的链表转移到新table中(JDK1.7转移后链表是逆序,JDK1.8转移后链表是原序)。链表的新index的计算方式为:hash&(newTable.length-1),hash是节点计算出的hash值,&是按位与,length是2的倍数,所以length-1在二进制来说每位都是1,这样可以保证最大的程度的散列hash值,于是得到结论:这新旧两次计算下标的结果,要不相同,要不就是新下标等于旧下标加上旧数组的长度。

14.Java类加载器

类加载器分类:Bootstrap ClassLoader加载java.util等核心类(使用C++编写),加载路径为JRE/lib/rt.jar;
Extension ClassLoader扩展类加载器,加载javax开头的等扩展类(使用Java编写),加载路径为JRE/lib/ext;
Application ClassLoader加载程序所在的目录下的类;
User ClassLoader用户自定义类加载器,可指定去哪加载类。

类装载的过程
(1)加载:ClassLoader加载class字节码文件,生产Class对象
(2)链接:校验(JVM 需要核验字节信息是否符合Java 虚拟机规范,防止恶意信息或者不合规信息危害JVM的运行)、准备(创建类或者接口中的静态变量,并初始化静态变量的初始值。这里的“初始化”和下面的初始化阶段是有区别的,侧重点在于分配所需要的内存空间,不会去执行更进一步的 JVM 指令)、解析(将常量池中的符号引用替换为直接引用)
(3)初始化:这一步真正去执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。

15.JVM详解以及堆和栈的区别【腾讯、字节】

Java程序在Java虚拟机(JVM)上运行。虚拟机能够屏蔽底层操作系统平台的不同并且减少基于原生语言开发的复杂性,使java能够跨各种平台,并且简单易用。一个Java虚拟机实例在运行过程中有三个子系统来保障它的正常运行,分别是类加载器子系统,执行引擎子系统和垃圾收集子系统。类加载器用来在运行时根据需要加载类。被加载到Java虚拟机内存中之后,虚拟机会读取并执行类里面存在的字节码指令。虚拟机中执行字节码指令的部分叫做执行引擎。最后,垃圾回收子系统负责自动释放没有用的对象。

JVM的内存分为五个区域:寄存器、本地方法区、方法区、栈内存、堆内存
(1) 方法区用于存储被JVM加载的类信息、常量、静态变量以及编译器编译后的代码等。方法区中有一个非常重要的部分就是运行时常量池,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。另外在运行期间也可将新的常量放入运行时常量池中。JVM规范对方法区的限制非常宽松,可以选择不实现垃圾收集。
(2) 本地方法区与栈内存类似,为虚拟机使用到的本地方法(JNI通过java本地接口与其他语言的代码交互)服务。
(3) 寄存器:一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。如果线程正在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是本地方法,这个计数器值为undefined。
(4) 堆内存是JVM所管理的内存中最大的一块,被进程的所有线程共享,在虚拟机启动时被创建。该区域用来存放对象实例,堆内存里的对象不会被随意释放, Java有垃圾回收机制不定时的收取。
(5) 栈内存是线程私有的,生命周期与线程相同。栈内存存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表、操作数栈、指向当前方法所属的类的运行时常量池的引用、方法返回地址和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。
区别:1)栈内存存储的是局部变量而堆内存存储的是实体;2)栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;3)栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

16.Java垃圾回收?CMS和G1的区别?【腾讯、字节】

CMS垃圾回收器(CMS, Concurrent Mark Sweep)并行标记清理垃圾回收器,是一种多并发低停顿的垃圾收集器,回收老年代内存。垃圾收集线程与工作线程并发执行,不对内存复制或也不压缩存活的对象。如果产生内存碎片问题,会通过FULL GC 方式进行垃圾回收。对内存划分为三个空间,年轻代(Young generation)分为1个新生代区(Eden)和2个存活区(survivor spaces)、老年代(Old generation)。同一时间内年轻代内存只会使用新生代区和一个存活区,垃圾回收时会将新生代区和存活区中的存活对象被拷贝到另一个空的存活区,当新生代对象存活时间达到阀值,对象会被提升到老年代。
当老年代内存占用率达到一定比率,CMS垃圾回收器就开始工作。老年代垃圾回收会经历以下过程:以一系列的名为GC Roots的对象(虚拟机栈中引用的对象,方法区中常量、静态属性引用的对象等)作为起始点,标记GC Roots能直接关联到(直接引用)的对象,此时需要暂停所有执行线程;进行GC Roots Tracing,遍历从GC Roots可达的所有对象,该阶段与工作线程并发执行;修正上一阶段因工作线程继续运作而导致标记产生变动的那一部分对象的标记,此时需要暂停所有执行线程;内存回收阶段,将死亡的内存对象占用的空间增加到一个空闲列表(free list),供以后的分配使用;清理数据结构,为下一次垃圾回收做准备。

G1回收器将堆进行分区,划分为一个个的区域,G1的收集过程如下:跟CMS一样,从roots出发(主要是survivor regions出发)遍历regions中的存活对象,这个阶段应用线程暂停;多线程同步遍历所有(包括年轻代)regions中的对象,如果发现某个region里的对象全是垃圾对象,则直接将regions进行释放;上个阶段中,可能产生新的存活对象或者垃圾对象,暂停应用,把上述对象遍历处理;回收都是垃圾的region,计算垃圾对象占比最多的regions,将其中的存活对象分散(evacuate)到其他region是中或者拷贝到空region当中,然后进行回收释放;复位,准备下一次垃圾回收过程。

区别:G1采用region的方式,实际上是清理压缩的过程,不会产生内存碎片;而CMS只清除,不会移动存活对象,可能会产生很多的内存碎片,除非full GC(对整个堆进行整理,效率较低)的情况;G1在垃圾回收的时候,可以根据预定的时间进行垃圾回收:根据时间限制,挑选垃圾最多的regions进行回收;CMS是一个全量过程,垃圾回收时间不能把控;这样G1就能灵活响应程序需求。

17.重载和重写?【阿里】

方法的重写和重载是java多态的不同表现,重写是父类与子类之间多态的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

18.Java基本类型?【阿里】

在 Java 中数据类型可以分为两大类:基本类型和引用类型。基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、short、int、long、float、double。引用类型则包括类、接口、数组、枚举等。Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆中,通过栈中的引用指向堆中存放的数据。

计算可存放的最大数,对于int是4字节32位,且int是有符号的,所以最大数是2的16次方减1。

19.Java设计模式(23种)中常见的几种?【阿里】

设计模式是针对特定场景给出的专家级的解决方案。常见的设计模式有:
单例模式:在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式好处是:对于某些类创建比较频繁,省去了多次创建的系统开销;省去了new操作符,降低了系统内存的使用频率,减轻垃圾回收机制压力;有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话会导致系统混乱。使用单例模式能保证核心交易服务器独立控制整个流程。单例模式大致分为两种:即懒汉式和饿汉式。
懒汉式在本地创建一个存储实例的变量,在该实例被调用时,先判断变量是否为空,若为空则创建一个类实例,若不为空则直接使用。饿汉式是当类装载时就创建类实例,不每次调用的时候无需判断,节省了运行时间。
关键:静态的私有的成员变量和构造方法,公有的获取实例的方法。
工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化方法延迟到其子类。工厂方法模式分为下三种:普通工厂模式就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。多个工厂方法模式是建立一个工厂类,对于实现了统一接口的一些类,工厂类中的每一个方法对应一个类的创建。静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建工厂类实例,直接调用即可。
设计模式可能会让编程实现。另外还有观察者模式、发布订阅模式、装饰器模式等等,可以去多了解几个

20.线程池

线程池帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。它可以提高响应速度。如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。重复利用。线程用完再放回池子,可以达到重复利用的效果,节省资源。
Java的线程池说一下,各个参数的作用,如何进行的?
corePoolSize:线程池核心线程数最大值。maximumPoolSize:线程池最大线程数大小。keepAliveTime:线程池中非核心线程空闲的存活时间大小。unit:线程空闲存活时间单位。workQueue:存放任务的阻塞队列。threadFactory:用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。handler:线程池的饱和策略事件,主要有四种类型。
线程池执行流程,即对应execute()方法:

四种拒绝策略:AbortPolicy(抛出一个异常,默认的),DiscardPolicy(直接丢弃任务),DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池),CallerRunsPolicy(交给线程池调用所在的线程进行处理)

说说几种常见的线程池及使用场景?
(1)newFixedThreadPool (固定数目线程的线程池):核心线程数和最大线程数大小一样,没有所谓的非空闲时间,即keepAliveTime为0,阻塞队列为无界队列LinkedBlockingQueue。提交任务,如果线程数少于核心线程,创建核心线程执行任务,如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列,如果线程执行完任务,去阻塞队列取任务,继续执行。适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。
(2)newCachedThreadPool(可缓存线程的线程池):核心线程数为0,最大线程数为Integer.MAX_VALUE,阻塞队列是SynchronousQueue,非核心线程空闲存活时间为60秒。提交任务,因为没有核心线程,所以任务直接加到SynchronousQueue队列,判断是否有空闲线程,如果有就取出任务执行,如果没有空闲线程,就新建一个线程执行,执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续活下去;否则被销毁。用于并发执行大量短期的小任务。
(3)newSingleThreadExecutor(单线程的线程池):核心线程数为1,最大线程数也为1,阻塞队列是无界队列LinkedBlockingQueue,keepAliveTime为0。提交任务,线程池是否有一条线程在,如果没有,新建线程执行任务,如果有,将任务加到阻塞队列。当前的唯一线程从队列取任务,执行完一个再继续取,一个人(一条线程)夜以继日地干活。适用于串行执行任务的场景,一个任务一个任务地执行。
(4)newScheduledThreadPool(定时及周期执行的线程池):最大线程数为Integer.MAX_VALUE,阻塞队列是DelayedWorkQueue,keepAliveTime为0,scheduleAtFixedRate() :按某种速率周期执行,scheduleWithFixedDelay():在某个延迟后执行。添加一个任务,线程池中的线程从DelayQueue中取任务,线程从DelayQueue中获取time大于等于当前时间的任务,执行完后修改这个任务的 time为下次被执行的时间,这个任务放回DelayQueue队列中。周期性执行任务的场景,需要限制线程数量的场景。

21.Java异常的类型?【阿里】


异常主要分为以下两种异常:
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是指RuntimeException以外的异常,类型上都属于 Exception 类及其子类。程序就不能编译通过。IOException和ClassNotFoundException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。

22.红黑树?【腾讯】

性质 1:节点非红即黑。
性质 2:根节点永远是黑色的。
性质 3:所有的叶节点都是空节点(即 null),并且是黑色的。
性质 4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
性质 5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。

Mysql篇

1.索引实现底层?为什么不用B树?B+树好在哪?【腾讯】

mysql默认存储引擎innodb只显式支持B-Tree(从技术上来说是B+Tree)索引,对于频繁访问的表,innodb会透明建立自适应hash索引,即在B树索引基础上建立hash索引,可以显著提高查找效率,
B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。
B和B+树的区别:第一就是查找元素时,即使在非叶子结点找到了目标值,它也只是用来索引的,还需要继续找到它在叶子结点的位置。第二就是如果要遍历,只需要遍历一次叶子结点即可。B+树的结构也十分适合范围查找,只需要找到范围的最小值所在位置,然后沿链表遍历即可。
B树的优点在于数据存储在每个结点中,可以更快访问到,而不必须走到叶子结点,B树更多的用在文件系统中。B+树的每个非叶子结点都只充当索引,所以查询必须到叶子结点结束,但它十分适合“扫库”和区间查找,而且因为大多结点只用于索引,所以并不会存储真正的数据,在内存上会更紧凑,相同的内存就可以存放更多的索引数据了。因此B+树更适合用来做数据库的索引。

Hash索引:检索效率非常高,索引的检索可以一次定位。但Hash索引只支持等值比较查询仅能满足"=",“IN"和”<=>"查询,不能使用范围查询。因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样。所以Hash索引无法被用来避免数据的排序操作。另外Hash索引不能利用部分索引键查询。对于组合索引,Hash索引在计算Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值。Hash索引在任何时候都不能避免表扫描。Hash索引是将索引键通过Hash运算之后,将 Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。所以Hash索引遇到大量Hash值相等的情况后性能并不一定就会比BTree索引高。

2.索引最左匹配原则知道吗,为什么?【字节】

当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+树是按照从左到右的顺序来建立搜索树的,遇到>、<、between、like就停止匹配。比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了。
Mysql创建联合索引首先会对第一个索引字段进行排序,再对第二个索引字段进行排序,所以第一个字段是有序的,而第二个字段就是无序的了。因此通常情况下,直接使用第二个字段进行条件判断是用不到索引的。

3.mysql 索引使用时注意什么?索引失效原因?【字节】

注意:优先使用唯一索引,为常用查询字段建索引,一张表的索引量不超过5个,表数据量少可以不用索引,尽量使用空间小的字段建索引,删除没用的索引。
失效原因:不满足最左匹配原则,范围索引列没有放在最后,使用了select *,索引列上有计算,索引列上使用了函数,字符类型没加引号,用is null和is not null没注意字段是否允许为空,like查询左边有%等等。

4.数据库中的四种事务隔离级别【腾讯】

事务的并发问题
(1)脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据;
(2)不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据的结果不一致;
(3)幻读:如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么操作第一个事务的用户会发现表中还有没有修改的数据行,就好像发生了幻觉一样;读到了之前不存在的行(间隙锁解决)
(4)丢失更新:指两个事务同时更新一行数据,后提交(或撤销)的事务将之前事务提交的数据覆盖了。
隔离级别
(1)Read Uncommitted(读未提交):如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新;
(2)Read Committed(读已提交):读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读;
(3)Repeatable Read(可重复读取):读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。此隔离级别可有效防止不可重复读和脏读;
(4)Serializable(串行化):它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。此隔离级别可有效防止脏读、不可重复读和幻读。但这个级别可能导致大量的超时现象和锁竞争,在实际应用中很少使用。

5.Mysql存储引擎Innodb和MyISAM的区别是什么?【腾讯】

Innodb支持事务,MyISAM不支持;Innodb支持外键,MyISAM不支持;Innodb主键索引的叶子节点是数据文件,辅助索引的叶子节点是主键的值,MyISAM 的主键索引和辅助索引,叶子节点都是数据文件的指针;Innodb不保存表的行数,执行select count(*) from tb需要全表扫描,MyISAM 用一个变量保存了整个表的行数,执行上述语句只需要读取该变量,速度很快。

6.什么是慢查询?如何处理慢查询?【字节】

帮助我们定位到那些真正形成瓶颈的慢查询进行优化。慢查询日志会记录所有执行时间超过 long_query_time(默认是 10s)的 SQL 及相关的信息。就可以定位到具体的业务接口并针对性的进行优化了。
把数据、日志、索引放到不同的I/O设备上,增加读取速度;纵向、横向分割表,减少表的尺寸;升级硬件;根据查询条件,建立索引,索引优化;提高网速;扩大服务器内存;分库分表。

7.简述数据库的事务和ACID【网易】

定义:事务就是一个对数据库操作的序列,是一个不可分割的工作单位,要不这个序列里面的操作全部执行,要不全部不执行。
特性:原子性 (Atomicity)指一个事务是一个不可分割的工作单位,要不全部执行,要不全部不执行;一致性(Consistency)指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏;隔离性(Isolation)指多个事务并发执行时,相互之间无影响;持久性(Durability)即当一个事提交后,对数据库的改变是永久性的,不会被回滚。

手撕代码篇

m×n二维数组,每行每列均有序,找出k所在位置。要求复杂度O(m+n),提示:把数组看成一张地图【字节】
sql:力扣数据库176.第二高的薪水【字节】
快排、堆排【字节、美团】
二分求有序可重复数组中k数出现次数【字节】
给定字符串a,b,判断b是否是a的子串【腾讯】
多线程实现生产者消费者【腾讯】
实现搜索二叉树【腾讯】
反转链表【网易】
总结:大数据岗位以及字节比较喜欢考察sql题,可以提前做做题;算法题一般是中等题或是简单题的最优解;另外还喜欢考察基础编程,如快排、多线程以及一些数据结构。

其它

1.分布式事务?2PC,3PC【字节】
2.spring cloud、spring boot基础

面试经验总结

1.准备自我介绍,包括毕业院校、项目经历等等。
2.介绍项目经历时遵循STAR原则:项目背景、希望达到什么目标、采取了什么行动、项目成果与反思。
3.对简历里写到的所有项目细节以及技术栈都要有一定的了解(技术栈如何应用到项目中)。面试官可能会问项目中遇到过什么困难,怎么解决的,这个需要提前想好。
4.除了腾讯(线上面试是腾讯会议,可以用idea编程),其他的都用面试网站上的编译器,只能肉眼debug,所以平时刷题的时候尽量别用idea来debug,提前适应。
5.大数据岗会问到Hadoop、Hive、Hbase、Spark、Kafka之类的,但问的不多,数据倾斜是热点问题,需要了解。面试官可能会选择其中一种框架稍微深挖,至于问哪个就需要你去引导了…
6.笔试成绩很重要,平时要多刷题。有些公司是会按笔试成绩刷人的。有些公司如果笔试做的非常出色,基本上面试也不会考察什么编程问题,基本上很好通过了。
本人面了五家大厂,拿到两个offer。感觉能力是一部分,但是面试表现和运气也是一部分了。有的时候刚好考到了你特别会的,就会特别有自信,那暂时没过也不要气馁,也许就是刚好问到了知识盲区,补回来就好了。祝大家都能拿到心仪的offer。

2021 Java后端+大数据暑期实习大厂面经相关推荐

  1. Java后端大数据 从0到1学习路线分析与规划

    在将要找工作之际,笔者根据大厂的招聘信息和在牛客网上的校招求职经验课程,在Java后端方向和大数据方向总结出如下内容,希望能对读者有所帮助. 目录 1.学习目标 1.1 后端开发方向 1.2 大数据开 ...

  2. 实习一年,自学从Java转向大数据开发

    大学如何自学java的文章: https://blog.csdn.net/CatchLight/article/details/112260371 第一份实习offer 专升本的第一学年末,凭借着自学 ...

  3. 大三、研二的秋招备战路线(Java、大数据)

    本文经授权转载自微信公众号:大数据肌肉猿 一.写作背景 二.秋招的意义 三.不同水平的同学备战秋招的策略(案例) 四.部分面试题整理 五.准备过程中的注意事项 六.针对简历复习 一.写作背景 1.分享 ...

  4. 1024 发福利,送你一份珍藏依旧的 Java,大数据礼包,确定不收藏 ?拒绝白嫖 !

    C 站的朋友们,大家好哇,我是 梦想家 Alex .又到了一年一度我们程序员的专属节日,祝大家 bug - - ,薪资 + + ,在走向人生巅峰,迎娶白富美的路上越走越远 ~ 哈哈,这在专属于技术人的 ...

  5. 谈谈Java与大数据之间的关系你们都了解了清楚了吗?

    Java是计算机编程语言界的王者,大数据是当下IT领域中最新潮的技术,Java和大数据都是当下十分受企业欢迎的IT技术,也是企业核心竞争力的重要组成部分,都说学大数据要先学Java,那么Java和大数 ...

  6. 布客·ApacheCN 编程/后端/大数据/人工智能学习资源 2020.9

    公告 ApacheCN 项目的最终目标:五年内备份并翻译 Github 上的所有教程(其实快被我们啃完了,剩下的不多了). 警告各位培训班:对 ApacheCN 宣传文章的举报,也将视为对 Apach ...

  7. Java知识大全 - 十二、Java和大数据

    Java 是大数据处理中使用最广泛的编程语言之一,因为它的性能.可扩展性和易用性.Java有几个库和框架为大数据处理提供支持,包括Apache Hadoop,Apache Spark,Apache S ...

  8. Java、大数据、Python哪个前景更好,薪资更高?

    都知道现在最火爆的是人工智能.大数据.而人工智能和大数据主要用的语言就是Java和Python.今天我们就来分析一下,当前java,python和大数据,哪个就业前景更好?自己该学哪一个? Java和 ...

  9. 基于Java毕业设计大数据文章发布系统源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计大数据文章发布系统源码+系统+mysql+lw文档+部署软件 基于Java毕业设计大数据文章发布系统源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S架构 ...

最新文章

  1. 计算机 大学活动 游戏,朝花夕拾”——中国矿业大学计算机学院积极举办“那些年我们一起玩过的游戏”活动...
  2. CO-类的本质、description方法
  3. mysql freebuf_浅析mysql存储过程
  4. python中列表相加规则_在Python字典列表中使用公共键/值求和值
  5. [c++primer][12]类
  6. gfs mysql_美国gfs数据介绍和解析
  7. Alfa与申威【江南所】
  8. 弹性容器中 子元素的flex属性介绍
  9. Serialization和Deserialization
  10. Shim特性是什么?
  11. android 按键 免root,按键精灵免root版
  12. 4核服务器型号,服务器厂商、型号、参数。
  13. 【课程表小程序源码】增加今日课表功能|开源代码
  14. 新浪云php与微信,新浪SAE php能够获取微信的头像,但是在本地运行代码获取不到?...
  15. python-App自动化测试框架——uiautomator2之元素操作方法(三)
  16. Python-ip代理
  17. windos 为什么会突然服务停止了_Win7系统print spooler服务总是自动停止怎么办?
  18. 一维向量转换为n维向量_如何在N维上固定万向节锁
  19. 字段类型与合理的选择字段类型
  20. clayui 仿苹果工具栏 完美解决图标毛边(黑边)

热门文章

  1. java sorted 方法_多个.sorted(…)调用java 8流
  2. 网络安全-靶机dvwa之sql注入Low到High详解(含代码分析)
  3. java 开发程序IDE
  4. 湿指纹识别技术 – 机遇与挑战并存
  5. Aleo Wagyu钱包
  6. (内含两种方式)Android 在线查看文档world丶xls丶ppt等文件
  7. 数据库分区、分表、分库,读写分离
  8. Redis源码初探(1)简单动态字符串SDS
  9. 微信计算机控制技术试卷,微型计算机控制技术试卷答案A.doc
  10. 3dsmax UVW展开,然后在 BodyPaint 3D 中进行绘制