1.内存泄漏和内存溢出

  • 内存溢出是指程序在申请内存时,内有足够的内存空间可以供其使用,比如申请了一个Integer,但是给它存了long类型才能存下的数,这就会造成内存溢出。
  • 内存泄漏是指在程序申请使用内存后没有归还,结果就是申请的那块内存地址丢了,系统就再也不能把他分配给需要的程序。一次内存泄漏可以忽略不计,但是内存泄漏堆积后果很严重,无论多少内存,迟早都会被占光。
  • 内存泄漏最终会导致内存溢出!

2.线程池的创建

线程池的创建有两种方式,《阿里巴巴Java开发手册》中强制线程池不允许使用Executor去创建,而是通过ThreadPoolExecutor的方式创建。

  • 方式一:通过ThreadPoolExecutor构造方法来实现
  • 方式二:通过Executor框架的工具类Executors来实现,可以通过该工具类创建三种类型的ThreadPoolExecutor:
    1. FixedThreadPool:
      1. 该方法返回一个固定线程数量的线程池。
      2. 该线程池中的线程数量始终不变。
      3. 当有一个新任务提交时,线程池中如果有线程空闲,则,立即执行。
      4. 如果没有,就把新的任务暂存在一个任务队列中,待有线程空闲时,再来处理队列中的任务。
    1. SingleThreadExecutor:
      1. 该方法返回只有一个线程的线程池。
      2. 当有一个新任务提交时,任务会被保存在任务队列中,等到线程空闲,再来处理队列中的任务
    1. CachedThreadPool:
      1. 该方法可以返回一个可根据实际情况调整线程数量的线程池。
      2. 线程池中的线程数量不确定,但是如果有空闲线程可以复用,会优先复用可复用的线程。
      3. 如果所有线程都在运行,又有新的任务提交,则会创建新的线程处理任务。
      4. 所有线程在当前任务执行完毕后,都会返回线程池进行复用。

3.volatile关键字

volatile关键字是JVM提供的轻量级的同步机制。可以理解为低配版的synchronized关键字。

  1. 保证了可见性。

    1. 一个线程修改了物理内存中的值,其他线程就会马上获得通知。
  2. 不保证原子性。

    1. 原子性是指不可分割,完整性,即某个线程在执行某个任务时是不可以被打断的。需要整体同时成功或者同时失败。
  3. 有序性(禁止指令重排)

    1. 就是在volatile「前后」加上「内存屏障」,使得编译器和CPU⽆法进⾏重排序,致使有序。

4.指令重排

指令重排序是指编译器或CPU为了优化程序的执行性能而对指令进行重新排序的一种手段,重排序会带来可见性问题,所以在多线程开发中必须要关注并规避重排序。

简单来说,就是指你在程序中写的代码,在执行时并不一定按照写的顺序,所以需要编译器或者CPU对指令进行重排序。

Java中的指令重排序有两次,第一次发生在将字节码编译成机器码的阶段,第二次发生在CPU执行的时候,也会适当对指令进行重排。


5.CAS

CAS(Compare and Swap),即比较并替换,是一个原子性的操作,是实现并发算法时常用到的一种技术。

CAS的思想:

  • 三个参数,一个内存值V、当前值A、即将更新的值B,当且仅当当前值A和内存值V相同时,将内存值V修改为B并返回true,否则什么都不做,并返回false。
  • 将当前值和内存值进行比对,判断是否被修改过,这就是CAS的核心。

为什么用?

  • synchronized通过加锁使得每次只会让一个线程去操作共享资源,但是CAS相当于没有加锁,多个线程都可以直接操作共享资源,在实际去修改的时候才会判断能否修改成功,在很多时候比synchronized锁要高效很多。
  • 比如对一个值进行累加,就没必要使用synchronized锁

缺点:ABA问题

  • 从CAS更新的时候,它只会⽐对当前值和内存值是否相等,这会ABA问题。

  • 例子:

    • 假设线程A读到当前值是10,可能线程B把值修改为100,然后线程C⼜把值修改为10。
    • 等到线程A拿到执⾏权时,因为当前值和内存值是⼀致的,线程A是可以修改的!
    • 站在线程A的⻆度来说,这个值是从未被修改的,但是其实这个变量已经被线程B和线程C修改过了。
    • 这就是ABA问题。

为了解决ABA的问题,Java提供了AtomicStampedReference类,在原有的基础上加了个版本比对,⽐对

的就是内存值+版本是否⼀致。


6.什么是控制反转,Spring如何实现控制反转?

  • 控制反转即IOC,就是把原有自己控制的事情交给别人去处理,更多的是一种思想或者设计模式,比如本来由我们⾃⼰new出来的对象,现在交由IOC容器,把对象的控制权交给它⽅了。

  • 在Spring中,Spring IOC 解决的是对象管理对象依赖的问题。

    • 本来是我们⾃⼰⼿动new出来的对象,现在则把对象交给Spring的IOC容器管理。
    • IOC容器可以理解为⼀个对象⼯⼚,我们都把该对象交给⼯⼚,⼯⼚管理这些对象的创建以及依赖关系
    • 等我们需要⽤对象的时候,从⼯⼚⾥边获取就好了。
    • 对象⽆需⾃⾏创建或者管理它的依赖关系,依赖关系将被「⾃动注⼊」到需要它们的对象当中去。
  • 控制反转是一种思想,依赖注入是实现方式。


7.什么是代理模式?

  • 定义:

    • 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
  • 作用:

    • 隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和被代理类对象(委托类对象)之间起到中介的作用。
    • 增加功能:代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

注意:代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

  • 分类:

    • 静态代理:静态代理需要⾃⼰写代理类,实现对应的接⼝。在我们运行之前,代理类.class文件就已经被创建了。
    • 动态代理:是在程序运行时通过反射机制动态创建的。

动态代理在技术或者框架原理中非常常见,比如MyBatis和SpringAOP技术。


8.MySQL如何优化?如何判断索引是否创建成功?

如何优化:

  • **创建合适的索引:**如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。

  • 开启慢查询日志:记录执行速度慢的SQL,对慢SQL语句进行优化

  • 优化SQL语句:

    • 尽量避免使用子查询
    • 用in来代替or
    • 读取适当的记录limit (m,n),不要读取多余的记录
    • 禁止不必要的Order By排序
  • 使用事务

  • 使用外键

如何判断索引是否创建成功?

  • 可以通过 explain 命令获取 select 语句的执行计划,通过 explain 我们可以知道表的读取顺序,数据读取操作的类型,哪些索引被使用了等信息。

9.HTTP是哪一层的协议?七层协议模型是哪七层?

OSI七层协议模型:

从下到上:物联网叔会使用

物理层,数据链路层,网络层,运输层,会话层,表示层,应用层

  • 应用层的任务是通过应用进程间的交互来完成特定网络应用。包括的协议有HTTP、DNS、SMTP。
  • 运输层的任务是负责向两台主机进程之间的通信提供通用的数据传输服务。包括的协议有TCP、UDP。
  • 网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。包括的协议有IP协议。
  • 数据链路层的任务是负责两台主机之间的数据传输,将网络层交下来的 IP 数据报组装成帧。
  • 物理层的任务是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。

10.JVM模型,JVM调优参数

JVM主要包含五大块,程序计数器、虚拟机栈、本地方法栈、堆和方法区。

其中程序计数器、虚拟机栈、本地方法栈是线程私有的,即线程之间相互隔离的。

堆和方法区是所有线程所共享的。

  • 程序计数器:

    • Java是多线程的语⾔,假设线程数⼤于CPU数,就很有可能有「线程切换」现象,切换意味着「中断」和「恢复」,那⾃然就需要有⼀块区域来保存「当前线程的执⾏信息」,程序计数器就是⽤于记录各个线程执⾏的字节码的地址(分⽀、循环、跳转、异常、线程恢复等都依赖于程序计数器)
  • 虚拟机栈:

    • 每个线程在创建的时候都会创建⼀个「虚拟机栈」,每次⽅法调⽤都会创建⼀个「栈帧」。每个「栈帧」会包含⼏块内容:局部变量表、操作数栈、动态连接和返回地址。
    • 虚拟机栈保存⽅法了局部变量、部分变量的计算并参与了⽅法的调⽤和返回。
  • 本地方法栈:

    • 本地⽅法栈跟虚拟机栈的功能类似,虚拟机栈⽤于管理 Java 函数的调⽤,⽽本地⽅法栈则⽤于管理本地⽅法的调⽤。这⾥的「本地⽅法」指的是「⾮Java⽅法」,⼀般本地⽅法是使⽤C语⾔实现的。
  • 堆:

    • 堆是所有线程共享的区域,⼏乎类的实例和数组分配的内存都来⾃于它
    • 堆划分为「新⽣代」和「⽼年代」,「新⽣代」⼜被进⼀步划分为 Eden 和 Survivor 区,最后
      Survivor 由 From Survivor 和 To Survivor 组成。
  • 方法区:

    • ⽅法区主要是⽤来存放已被虚拟机加载的「类相关信息」:包括类信息、常量池。
      • 类信息⼜包括了类的版本、字段、⽅法、接⼝和⽗类等信息。
      • 常量池⼜可以分「静态常量池」和「运⾏时常量池」
        • 静态常量池主要存储的是「字⾯量」以及「符号引⽤」等信息,静态常量池也包括了我们说的「字符串常
          量池」。
        • 运⾏时常量池存储的是「类加载」时⽣成的「直接引⽤」等信息。

11.字符串去重的几种方法

  • 暴力循环
  • 集合

12.浅拷贝和深拷贝

  • 浅拷贝:只复制当前对象的基本数据类型和引用变量,但是引用变量指向的那些对象不进行复制,引用变量指向的仍然是原对象中引用变量所指向的值。修改克隆对象可能回影响原对象,不安全。
  • 深拷贝:完全拷贝了基本数据类型和引用数据类型以及引用数据类型指向的对象,也进行了复制。也就是说深拷贝把要复制的对象所引用的对象也全部进行了复制。