阿里面经

阿里电面(java研发)

  1. 从下往上说一下OSI七个分层?
  • 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
  1. TCP和UDP的区别

    • TCP提供面向连接的、可靠的数据流传输,而UDP提供的是非面向连接的、不可靠的数据流传输。(TCP发出去还会问候核实一下以确保安全; UDP发出去就不管了 )
    • TCP传输单位称为TCP报文段,UDP传输单位称为用户数据报。
    • TCP注重数据安全性,UDP数据传输快,因为不需要连接等待,少了许多操作,但是其安全性却一般。
  2. 说说数据库连接(join)?

    • Inner join–产生的是AB两个集合的交集
    • left[outer]join–产生A的完全集,而B中匹配的则有值,没有匹配的则返回null
    • right[outer]join–产生B的完全集,而A中匹配的则有值,没有匹配的则返回null
  3. 说说事务?

    • 所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
  4. HashMap原理?

    • 底层是数组加链表实现的哈希表。允许null作为键,null作为值。线程不安全。

    • 为什么用数组+链表实现?

      利用拉链法解决冲突:把所有的同义词用单链表链接起来。该方法下,哈希表每个单元中存放的不再是元素本身,而是相应同义词单链表的头指针。

    • HashMap维护了一个Entry数组,Entry内部类有key,value,hash和next四个字段,其中next也是一个Entry类型。可以将Entry数组理解为一个个的散列桶。每一个桶实际上是一个单链表。当执行put操作时,会根据key的hashcode定位到相应的桶。 遍历单链表检查该key是否已经存在,如果存在,覆盖该value,反之,新建一个新的Entry,并放在单链表的头部。当通过传递key调用get方法时,它再次使用key.hashCode()来找到相应的散列桶,然后使用key.equals()方法找出单链表中正确的Entry,然后返回它的值。

    • HashMap:线程不同步。根据key的hashcode进行存储,内部使用静态内部类Node的数组进行存储,默认初始大小为16,每次扩大一倍。当发生Hash冲突时,采用拉链法(链表)。可以接受为null的键值(key)和值(value)。JDK1.8中:当单个桶中元素个数大于等于8时,链表实现改为红黑树实现;当元素个数小于6时,变回链表实现。由此来防止hashCode攻击。

    • Hashtable是线程安全的。ConcurrentHashMap 针对读操作做了大量的优化。通过 HashEntry 对象的不变性和用 volatile 型变量协调线程间的内存可见性,使得 大多数时候,读操作不需要加锁就可以正确获得值。这个特性使得 ConcurrentHashMap 的并发性能在分离锁的基础上又有了近一步的提高。ConcurrentHashMap 的高并发性主要来自于三个方面:

    • 用分离锁实现多个线程间的更深层次的共享访问。

    • 用 HashEntery 对象的不变性来降低执行读操作的线程在遍历链表期间对加锁的需求。
    • 通过对同一个 Volatile 变量的写 / 读访问,协调不同线程间读 / 写操作的内存可见性。
  5. Java运行时数据区域?

    • 包括程序计数器、JVM栈、本地方法栈、方法区、堆
  6. 方法区里存放什么?

    • 由于程序计数器、JVM栈、本地方法栈3个区域随线程而生随线程而灭,对这几个区域内存的回收和分配具有确定性。 而方法区和堆则不一样,程序需要在运行时才知道创建哪些对象,对这部分内存的分配是动态的,GC关注的也就是这部分内存。
  • 本地方法栈:和jvm栈所发挥的作用类似,区别是jvm栈为jvm执行java方法(字节码)服务,而本地方法栈为jvm使用的native方法服务。
  • JVM栈:局部变量表、操作数栈、动态链接、方法出口。
    • 方法区:用于存储已被虚拟机加载的类信息,常量、静态变量、即时编译器编译后的代码等。
    • 堆:存放对象实例。
  1. 怎样判断是否需要收集?

    • 引用计数法:对象没有任何引用与之关联(无法解决循环引用)
    • 可达性分析法:通过一组称为GC Root的对象为起点,从这些节点向下搜索,如果某对象不能从这些根对象的一个(至少一个)所到达,则判定该对象应当回收。
  2. 什么可作为GCRoot的对象?

    • 虚拟机栈中引用的对象。方法区中类静态属性引用的对象,方法区中类常量引用的对象,本地方法栈中JNI引用的对象。
  3. Spring IOC/AOP?

  • AOP(Aspect-OrientedProgramming,面向方面编程,可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。 OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。 也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码, 在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。依赖注入(Dependency Injection)控制反转(Inversion of Control)是同一个概念.当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例.但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入.不管是依赖注入,还是控制反转,都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。 在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)。
  1. 自己在应用中写过什么切面?

AOP使用场景:

  • Authentication 权限
  • Caching 缓存
    • Context passing 内容传递
    • Error handling 错误处理
    • Lazy loading 懒加载
    • Debugging  调试
    • logging, tracing, profiling and monitoring 记录跟踪 优化 校准
    • Performance optimization 性能优化
    • Persistence  持久化
    • Resource pooling 资源池
    • Synchronization 同步
    • Transactions 事务
  1. JVM如何加载一个类的过程,双亲委派模型中有哪些方法?

    • 加载:定位要加载的类文件,并将其字节流装载到JVM中;
    • 链接:给要加载的类分配最基本的内存结构保存其信息,比如属性,方法以及引用的类。在该阶段,该类还处于不可用状态;验证:对加载的字节流进行验证,比如格式上的,安全方面的;内存分配:为该类准备内存空间来表示其属性,方法以及引用的类;解析:加载该类所引用的其它类,比如父类,实现的接口等。
    • 初始化:对类变量进行赋值。
    • getParent(),findLoadedClass(),LoadClass(),findBootstrapClassOrNull(),findClass(),resolveClass()
  2. 进程间通信有哪几种方式?

    | 类型 | 无连接 | 可靠 | 流控制 | 优先级 |
    | ————- | —- | —- | —- | —- |
    | 消息队列 | N | Y | Y | Y |
    | 信号量 | N | Y | Y | Y |
    | 共享内存 | N | Y | Y | Y |
    | UNIX流SOCKET | N | Y | Y | N |
    | UNIX数据包SOCKET | Y | Y | N | N |
    | 普通PIPE | N | Y | Y | N |
    | 流PIPE | N | Y | Y | N |
    | 命名PIPE(FIFO) | N | Y | Y | N |

  3. Linux下如何进行进程调度的?

    • 实时进程的调度

      不同调度策略的实时进程只有在相同优先级时才有可比性:

      • 对于FIFO的进程,意味着只有当前进程执行完毕才会轮到其他进程执行。由此可见相当霸道。
      • 对于RR的进程。一旦时间片消耗完毕,则会将该进程置于队列的末尾,然后运行其他相同优先级的进程,如果没有其他相同优先级的进程,则该进程会继续执行。
    • 非实时进程调度

      Linux对普通的进程,根据动态优先级进行调度。而动态优先级是由静态优先级(static_prio)调整而来(考虑了进程的属性)。Linux下,静态优先级是用户不可见的,隐藏在内核中。

    • 现代方法CFS

      不再单纯依靠进程优先级绝对值,而是参考其绝对值,综合考虑所有进程的时间,给出当前调度时间单位内其应有的权重,也就是,每个进程的权重X单位时间=应获cpu时间,但是这个应得的cpu时间不应太小(假设阈值为1ms),否则会因为切换得不偿失。但是,当进程足够多时候,肯定有很多不同权重的进程获得相同的时间——最低阈值1ms,所以,CFS只是近似完全公平。

  4. 什么是一致性哈希?

    • 将 Item 均匀地分布(Even Distribution)到不同的 Bucket 中,也就是负载均衡(Load Balancing)
    • 一致性哈希的实现:
      • 对所有的 Buckets 和 Items 应用相同的哈希函数 H,按照哈希值的顺序把它们排列到一条线上,然后将距离 Bucket 最近的 Items 都放入该 Bucket 中。
      • 另一种实现方式是把哈希值的最大值和最小值连接到一起,形成一个哈希环(Consistent Hashing Ring),按照顺时针方向将 Items 放入 Bucket 中。
  5. linux中socket编程,底层实现?

    • 对于应用TCP/IP协议的应用程序,UNIX BSD提供了SOCKET的应用编程接口。
    • socket起源于Unix,而Unix/linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
    • 当应用程序要创建一个套接字时,操作系统就返回一个小整数作为描述符,应用程序则使用这个描述符来引用该套接字需要I/O请求的应用程序请求操作系统打开一个文件。操作系统就创建一个文件描述符提供给应用程序访问文件。从应用程序的角度看,文件描述符是一个整数,应用程序可以用它来读写文件。
  6. socket连接数目的瓶颈?

    Server的内存

  7. java中的软引用,弱引用?

    • 如果一个对象只具有软引用(SoftReference),则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

       String str=new String("abc");                                     // 强引用
       SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用
      
    • 弱引用(WeakReference)与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

      String str=new String("abc");    
      WeakReference<String> abcWeakRef = new WeakReference<String>(str);
      str=null;
      
  8. 了解rpc吗?

    • RPC: 远程过程调用(Remote Procedure Call
    • 远程过程调用采用客户机/服务器(C/S)模式。请求程序就是一个客户机,而服务提供程序就是一台服务器。和常规或本地过程调用一样,远程过程调用是同步操作,在远程过程结果返回之前,需要暂时中止请求程序。使用相同地址空间的低权进程或低权线程允许同时运行多个远程过程调用。
  9. 什么是epoll?

    • epoll是linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
    • 提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
    • 优点:
      • 支持一个进程打开大数目的socket描述符
      • IO效率不随FD数目增加而线性下降
      • 使用mmap加速内核与用户空间的消息传递