1. 计算机基础知识
    1. 计算机组成原理
    2. 操作系统
      1. 进程管理
        1. 进程间的通信方式
          1. 1、管道/匿名管道(Pipes)
          2. 2、有名管道(Named Pipes)
          3. 3、信号(Signal)
          4. 4、消息队列(Message Queuing)
          5. 5、信号量(Semaphores)
          6. 6、共享内存(Shared memory)
          7. 7、套接字(Sockets)
        2. 线程间的同步方式
          1. 1、互斥量(Mutex)
          2. 2、信号量(Semaphore)
          3. 3、事件(Event)
        3. 进程间的调度算法
          1. 1、先到先服务(FCFS)调度算法
          2. 2、短作业优先(SJF)的调度算法
          3. 3、时间片轮转调度算法
          4. 4、多级反馈队列调度算法
          5. 5、优先级调度
        4. 死锁
          1. 死锁产生的四个必要条件
          2. 1、互斥条件
          3. 一个资源每次只能被一个进程使用,不能同时被多个进程共享。
          4. 2、请求和保持条件
          5. 一个进程在请求新的资源时,不放弃已经占有的资源。
          6. 3、不可剥夺条件
          7. 一个进程已经获得的资源,在使用完之前不能被其他进程强行夺走。
          8. 4、循环等待条件
          9. 若干个进程之间形成一个环路,每个进程都在等待下一个进程占有的资源。
      2. 内存管理
      3. 虚拟内存
    3. 计算机网络
      1. OSI七层模型
        1. 应用层 (为计算机用户提供服务)
        2. 表示层 (数据处理,编解码、加密解密、压缩解压缩)
        3. 会话层 (管理(建立、维护、重连)应用程序之间的会话)
        4. 传输层 (为两台主机进程之间的通信提供通用的数据传输服务)
        5. 网络层 (路由和寻址(决定数据在网络的游走路径))
        6. 数据链路层 (帧编码和误差纠正控制)
        7. 物理层 (透明的传送比特流传输)
        8. 子主题 8
      2. TCP/IP网络
        1. 应用层
          1. 应用层位于传输层之上,主要提供两个终端设备上的应用程序之间信息交换的服务,它定义了信息交换的格式,消息会交给下一层传输层来传输。 我们把应用层交互的数据单元称为报文。
          2. 应用层协议定义了网络通信规则,对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如支持 Web 应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等。
          3. 常用协议
          4. HTTP:超文本传输协议
          5. SMTP:简单邮件传输(发送)协议
          6. POP3/IMAP:邮件接收的协议
          7. FTP:文件传输协议
          8. Telnet:远程登陆协议
          9. SSH:安全的网络传输协议
        2. 传输层
          1. 传输层的主要任务就是负责向两台终端设备进程之间的通信提供通用的数据传输服务。 应用进程利用该服务传送应用层报文。“通用的”是指并不针对某一个特定的网络应用,而是多种应用可以使用同一个运输层服务。
          2. 运输层主要使用以下两种协议: 1、传输控制协议 TCP(Transmisson Control Protocol)--提供 面向连接 的,可靠的 数据传输服务。 2、用户数据协议 UDP(User Datagram Protocol)--提供 无连接 的,尽最大努力的数据传输服务(不保证数据传输的可靠性)。
          3. TCP的三次握手四次挥手
          4. TCP首部,20字节的固定首部
          5. TCP的标志位
          6. SYN
          7. SYN=1 代表一个连接请求或者连接接受报文
          8. ACK
          9. 表示确认收到对方的报文,回复对方的序列号加一
          10. PSH
          11. 表示接收方应该尽快将这个报文交给应用层
          12. FIN
          13. 表示释放一个连接,占用一个序列号
          14. RST
          15. 表示重置连接
          16. URG
          17. 表示紧急指针有效
          18. TCP的状态
          19. CLOSED:初始状态,表示TCP连接是关闭着的或未打开的。
          20. LISTEN:表示服务端的某个SOCKET处于监听状态,可以接受客户端的连接。
          21. SYN_SENT:表示客户端发送了SYN报文,并等待服务端的SYN和ACK报文
          22. SYN_RCVD:表示服务端接收到了客户端的SYN报文,并且回复SYN和ACK报文。
          23. ESTABLISHED:表示TCP连接已经成功建立,开始传输数据
          24. FIN_WAIT_1:表示客户端主动关闭连接,并发送了FIN报文,等待服务器端的ACK或FIN报文。
          25. FIN_WAIT_2:表示客户端收到了服务端的ACK报文,并等待服务端的FIN报文。
          26. CLOSE_WAIT:表示服务器端收到了客户端的FIN报文,并回复了ACK报文,等待关闭连接。
          27. LAST_ACK:表示服务端发送了FIN报文,并等待客户端的ACK报文。
          28. TIME_WAIT:表示客户端收到了服务器端的FIN报文,并恢复了ACK报文,等待一段时间后关闭连接。
          29. CLOSING:表示双方同时发送了FIN报文,并互相回复了ACK报文。
          30. TCP三次握手
          31. 为什么是三次?不是四次?
          32. TCP四次挥手
          33. 子主题 1
        3. 网络层
          1. 网络层负责为分组交换网上的不同主机提供通信服务。 在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报,简称数据报。 ⚠️注意 :不要把运输层的“用户数据报 UDP”和网络层的“IP 数据报”弄混。
          2. 网络层的还有一个任务就是选择合适的路由,使源主机运输层所传下来的分组,能通过网络层中的路由器找到目的主机。
          3. 这里强调指出,网络层中的“网络”二字已经不是我们通常谈到的具体网络,而是指计算机网络体系结构模型中第三层的名称。互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Internet Prococol)和许多路由选择协议,因此互联网的网络层也叫做 网际层 或 IP 层。
          4. 网络层常见协议
          5. IP:网际协议 :网际协议 IP 是TCP/IP协议中最重要的协议之一,也是网络层最重要的协议之一,IP协议的作用包括寻址规约、定义数据包的格式等等,是网络层信息传输的主力协议。目前IP协议主要分为两种,一种是过去的IPv4,另一种是较新的IPv6,目前这两种协议都在使用,但后者已经被提议来取代前者。
          6. ARP 协议 :ARP协议,全称地址解析协议(Address Resolution Protocol),它解决的是网络层地址和链路层地址之间的转换问题。因为一个IP数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但IP地址属于逻辑地址,而MAC地址才是物理地址,ARP协议解决了IP地址转MAC地址的一些问题。
          7. NAT:网络地址转换协议 :NAT协议(Network Address Translation)的应用场景如同它的名称——网络地址转换,应用于内部网到外部网的地址转换过程中。具体地说,在一个小的子网(局域网,LAN)内,各主机使用的是同一个LAN下的IP地址,但在该LAN以外,在广域网(WAN)中,需要一个统一的IP地址来标识该LAN在整个Internet上的位置。
        4. 网络接口层
          1. 我们可以把网络接口层看作是数据链路层和物理层的合体。
          2. 数据链路层(data link layer)通常简称为链路层( 两台主机之间的数据传输,总是在一段一段的链路上传送的)。数据链路层的作用是将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
          3. 物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异
    4. 数据结构
    5. 算法
  2. Java基础
    1. 基础
    2. 容器
      1. Collection
        1. List
        2. Set
        3. Queue
      2. Map
    3. 并发
      1. 线程的状态
        1. NEW: 初始状态,线程被创建出来但没有被调用 start() 。 RUNNABLE: 运行状态,线程被调用了 start()等待运行的状态。 BLOCKED :阻塞状态,需要等待锁释放。 WAITING:等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。 TIME_WAITING:超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。 TERMINATED:终止状态,表示该线程已经运行完毕。
      2. 死锁
        1. 死锁的四个必要条件
          1. 1、互斥条件:该资源任意一个时刻只由一个线程占用。
          2. 2、请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
          3. 3、不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
          4. 4、循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
        2. 预防和避免线程死锁
          1. 1、破坏请求与保持条件 :一次性申请所有的资源。
          2. 2、破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
          3. 3、破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
      3. JMM内存模型
        1. 子主题 1
    4. IO
      1. 1、IO基础知识
      2. 2、IO设计模式
      3. 3、IO模型
        1. 冯诺依曼结构,分为运算器、控制器、存储器、输入设备、输出设备
        2. 从计算机结构的视角来看的话, I/O 描述了计算机系统与外部设备之间通信的过程。我们再先从应用程序的角度来解读一下 I/O。根据大学里学到的操作系统相关的知识:为了保证操作系统的稳定性和安全性,一个进程的地址空间划分为 用户空间(User space) 和 内核空间(Kernel space ) 。像我们平常运行的应用程序都是运行在用户空间,只有内核空间才能进行系统态级别的资源有关的操作,比如文件管理、进程通信、内存管理等等。也就是说,我们想要进行 IO 操作,一定是要依赖内核空间的能力。并且,用户空间的程序不能直接访问内核空间。当想要执行 IO 操作时,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。因此,用户进程想要执行 IO 操作的话,必须通过 系统调用 来间接访问内核空间我们在平常开发过程中接触最多的就是 磁盘 IO(读写文件) 和 网络 IO(网络请求和响应)。从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起 IO 调用(系统调用),操作系统负责的内核执行具体的 IO 操作。也就是说,我们的应用程序实际上只是发起了 IO 操作的调用而已,具体 IO 的执行是由操作系统的内核来完成的。 当应用程序发起 I/O 调用后,会经历两个步骤:内核等待 I/O 设备准备好数据。 内核将数据从内核空间拷贝到用户空间。
        3. 常见IO模型
          1. 同步阻塞IO
          2. 同步非阻塞IO
          3. I/O多路复用
          4. 信号驱动I/O
          5. 异步I/O
        4. Java中常见的三种I/O模型
          1. BIO(同步阻塞I/O)
          2. 同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。 在客户端连接数量不高的情况下,是没问题的。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
          3. NIO(Non-blocking/New I/O)
          4. Java 中的 NIO 于 Java 1.4 中引入,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它是支持面向缓冲的,基于通道的 I/O 操作方法。 对于高负载、高并发的(网络)应用,应使用 NIO 。Java 中的 NIO 可以看作是 I/O 多路复用模型。
          5. AIO(异步I/O)
    5. JVM
      1. Java内存区域(运行时数据区)
        1. 线程私有
          1. 程序计数器
          2. 虚拟机栈
          3. 本地方法栈
        2. 线程共享
          1. 方法区
          2. 运行时常量池
      2. 对象的相关知识
        1. Java对象的创建过程
          1. 1、类加载检查
          2. 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
          3. 2、分配内存
          4. 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
          5. 内存分配的两种方式
          6. 1、指针碰撞
          7. 适用场合 :堆内存规整(即没有内存碎片)的情况下。 原理 :用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。 使用该分配方式的 GC 收集器:Serial, ParNew
          8. 2、空闲列表
          9. 适用场合 : 堆内存不规整的情况下。 原理 :虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例,最后更新列表记录。 使用该分配方式的 GC 收集器:CMS
          10. 选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的。
          11. 内存分配并发问题(两种方式保证线程安全)
          12. CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
          13. TLAB: 为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配。
          14. 3、初始化零值
          15. 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
          16. 4、设置对象头
          17. 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
          18. 5、执行init方法
          19. 在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
        2. 对象的内存布局
          1. 在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:对象头、实例数据和对齐填充。Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,换句话说就是对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
        3. 对象的访问定位
          1. 建立对象就是为了使用对象,我们的 Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有:使用句柄、直接指针。
      3. Java垃圾回收
        1. 内存分配和回收原则
          1. 1、大多数情况下,对象在新生代Eden区分配。当Eden区没有足够的空间分配时,虚拟机将进行一次MinorGC(针对新生代的垃圾回收)。
          2. 2、如果对象无法放入Survivor区,就会通过分配担保机制把新生代的对象提前转移到老年代中去。 如果是大对象(需要大量连续的内存空间)的话,会直接进入老年代,为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。
          3. 分配担保: JDK 6 Update 24 之前,在发生 Minor GC 之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次 Minor GC 可以确保是安全的。如果不成立,则虚拟机会先查看 -XX:HandlePromotionFailure 参数的设置值是否允许担保失败(Handle Promotion Failure);如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者 -XX: HandlePromotionFailure 设置不允许冒险,那这时就要改为进行一次 Full GC。JDK 6 Update 24 之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行 Minor GC,否则将进行 Full GC。
          4. 3、长期存活的对象将会被放到老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄计数器。对象在Eden区出生并存活过第一次MinorGC,对象年龄就会变为1,然后在Survivor区每存活过一次MinorGC,对象的年龄就会加一,如果年龄到了15,则会被晋升到老年代中。
          5. 关于默认的晋升年龄是 15,这个说法的来源大部分都是《深入理解 Java 虚拟机》这本书。 如果你去 Oracle 的官网阅读相关的虚拟机参数open in new window,你会发现-XX:MaxTenuringThreshold=threshold这里有个说明Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector, and 6 for the CMS collector.默认晋升年龄并不都是 15,这个是要区分垃圾收集器的,CMS 就是 6.
        2. 死亡对象判断方法
          1. 1、引用计数法。
          2. 给每个对象添加一个引用计数器,每当有一个地方引用,计数器加一;当引用失效,计数器减一;任何时间计数器为0的对象就是不再被引用的。
          3. 2、可达性分析法。
          4. 从“GC ROOTS”对象作为起点,从这些节点开始向下搜索,节点走过的路径称为引用链,当一个对象到GC ROOTS没有任何引用链的话,则证明此对象是不可用的,需要被回收。
          5. 哪些对象可以作为GC ROOTS
          6. 1、虚拟机栈(栈帧中的本地变量表)中引用的对象 2、本地方法栈(Native 方法)中引用的对象 3、方法区中类静态属性引用的对象 4、方法区中常量引用的对象 5、所有被同步锁持有的对象
          7. 引用类型
          8. 1、强引用
          9. 以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
          10. 2、软引用
          11. 如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。
          12. 3、弱引用
          13. 如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
          14. 4、虚引用
          15. "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
        3. 垃圾收集算法
          1. 标记-清除算法
          2. 标记-复制算法
          3. 标记-整理算法
          4. 分代收集算法
          5. 分代收集含义是,将内存区域分为不同的年龄,分开进行垃圾收集,比如在新生代使用标记-复制算法收集,在老年代使用标记-清除或者标记-整理算法收集
        4. 垃圾收集器
          1. Serial收集器
          2. 单线程收集器,在其运行时会stop the world,但是没有线程切换,效率高。 新生代使用标记-复制算法,老年代使用标记-整理算法。
          3. ParNew收集器
          4. Serial收集器的多线程版本
          5. Parallel Scavenge收集器
          6. 使用标记-复制算法的多线程收集器 -XX:+UseParallelGC:使用 Parallel 收集器+ 老年代串行 -XX:+UseParallelOldGC:使用 Parallel 收集器+ 老年代并行 JDK1.8默认垃圾收集器(Parallel Scavenge + Parallel Old)
          7. Serial Old收集器
          8. Parallel Old收集器
          9. CMS收集器
          10. G1收集器
          11. ZGC收集器
        5. 类文件结构
        6. 类加载过程(see:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4)
          1. 加载
          2. 1、通过全类名获取定义此类的二进制字节流。
          3. 2、将字节流代表的静态存储结构转换为方法区的运行时数据结构。
          4. 3、在内存中生成一个代表 该类的Class对象,作为方法区这些数据的访问入口。
          5. 链接
          6. 验证
          7. 1、文件格式验证
          8. 验证字节流是否符合Class文件的格式的规范。
          9. 2、元数据验证
          10. 对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
          11. 3、字节码验证
          12. 最复杂的一个阶段,通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
          13. 4、符号引用验证
          14. 确保解析动作能正确执行
          15. 准备
          16. 准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
          17. 1、这时候进行内存分配的仅包括类变量( Class Variables ,即静态变量,被 static 关键字修饰的变量,只与类相关,因此被称为类变量),而不包括实例变量。实例变量会在对象实例化时随着对象一块分配在 Java 堆中。
          18. 2、从概念上讲,类变量所使用的内存都应当在 方法区 中进行分配。不过有一点需要注意的是:JDK 7 之前,HotSpot 使用永久代来实现方法区的时候,实现是完全符合这种逻辑概念的。 而在 JDK 7 及之后,HotSpot 已经把原本放在永久代的字符串常量池、静态变量等移动到堆中,这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。
          19. 3、这里所设置的初始值"通常情况"下是数据类型默认的零值(如 0、0L、null、false 等),比如我们定义了public static int value=111 ,那么 value 变量在准备阶段的初始值就是 0 而不是 111(初始化阶段才会赋值)。特殊情况:比如给 value 变量加上了 final 关键字public static final int value=111 ,那么准备阶段 value 的值就被赋值为 111。
          20. 解析
          21. 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。在程序实际运行时,只有符号引用是不够的,举个例子:在程序执行方法时,系统需要明确知道这个方法所在的位置。Java 虚拟机为每个类都准备了一张方法表来存放类中所有的方法。当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。综上,解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。
          22. 初始化
          23. 初始化阶段是执行初始化方法 <clinit> ()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。说明: <clinit> ()方法是编译之后自动生成的。 对于<clinit> () 方法的调用,虚拟机会自己确保其在多线程环境中的安全性。因为 <clinit> () 方法是带锁线程安全,所以在多线程环境下进行类初始化的话可能会引起多个线程阻塞,并且这种阻塞很难被发现。
          24. 对于初始化阶段,虚拟机严格规范了有且只有 5 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):
          25. 1、当遇到 new 、 getstatic、putstatic 或 invokestatic 这 4 条直接码指令时,比如 new 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。 --当 jvm 执行 new 指令时会初始化类。即当程序创建一个类的实例对象。 --当 jvm 执行 getstatic 指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。 --当 jvm 执行 putstatic 指令时会初始化类。即程序给类的静态变量赋值。 --当 jvm 执行 invokestatic 指令时会初始化类。即程序调用类的静态方法。
          26. 2、使用 java.lang.reflect 包的方法对类进行反射调用时如 Class.forname("..."), newInstance() 等等。如果类没初始化,需要触发其初始化。
          27. 3、初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
          28. 4、当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。
          29. 5、MethodHandle 和 VarHandle 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用, 就必须先使用 findStaticVarHandle 来初始化要调用的类。
          30. 6、当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
          31. 使用
          32. 卸载
          33. 卸载类即该类的 Class 对象被 GC。卸载类需要满足 3 个要求:该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。该类没有在其他任何地方被引用该类的类加载器的实例已被 GC所以,在 JVM 生命周期内,由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的。只要想通一点就好了,jdk 自带的 BootstrapClassLoader, ExtClassLoader, AppClassLoader 负责加载 jdk 提供的类,所以它们(类加载器的实例)肯定不会被回收。而我们自定义的类加载器的实例是可以被回收的,所以使用我们自定义加载器加载的类是可以被卸载掉的。
        7. 类加载器
          1. JVM内置类加载器
          2. 1、BootstrapClassLoader 启动类加载器
          3. 最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者被 -Xbootclasspath参数指定的路径中的所有类。
          4. 2、ExtensionClassLoader 扩展类加载器
          5. 主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
          6. 3、AppClassLoader 应用程序类加载器
          7. 面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。
          8. 双亲委派模型
          9. 自底向上检查类是否被加载 自顶向下尝试加载类
          10. 双亲委派模型的好处: 双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。
          11. 自定义加载器的话,需要继承 ClassLoader 。如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法
          12. 除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。如果我们要自定义自己的类加载器,很明显需要继承 ClassLoader。
        8. JVM参数
          1. 堆内存
          2. 1、显示指定堆内存 -Xms -Xmx,例如 : -Xms200m -Xmx200m
          3. 2、显示新生代内存: -XX:NewSize=256m -XX:MaxNewSize=256m 或者 -Xmn256m
          4. GC 调优策略中很重要的一条经验总结是这样说的:将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC 日志分析新生代空间大小分配是否合理,适当通过“-Xmn”命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。 可以通过-XX:NewRatio=1 设置老年代和新生代内存的比值。
          5. 3、显示指定永久代/元空间大小 -XX:PermSize=N #方法区 (永久代) 初始大小 -XX:MaxPermSize=N #方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen
          6. 垃圾收集
          7. 1、垃圾回收器 串行垃圾收集器 并行垃圾收集器 CMS 垃圾收集器 G1 垃圾收集器 分别是 -XX:+UseSerialGC -XX:+UseParallelGC -XX:+UseParNewGC -XX:+UseG1GC
          8. 2、GC日志记录 # 必选 # 打印基本 GC 信息 -XX:+PrintGCDetails -XX:+PrintGCDateStamps # 打印对象分布 -XX:+PrintTenuringDistribution # 打印堆数据 -XX:+PrintHeapAtGC # 打印Reference处理信息 # 强引用/弱引用/软引用/虚引用/finalize 相关的方法 -XX:+PrintReferenceGC # 打印STW时间 -XX:+PrintGCApplicationStoppedTime # 可选 # 打印safepoint信息,进入 STW 阶段之前,需要要找到一个合适的 safepoint -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 # GC日志输出的文件路径 -Xloggc:/path/to/gc-%t.log # 开启日志文件分割 -XX:+UseGCLogFileRotation # 最多分割几个文件,超过之后从头文件开始写 -XX:NumberOfGCLogFiles=14 # 每个文件上限大小,超过就触发分割 -XX:GCLogFileSize=50M
          9. 处理OOM
          10. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./java_pid<pid>.hprof -XX:OnOutOfMemoryError="< cmd args >;< cmd args >" -XX:+UseGCOverheadLimit HeapDumpOnOutOfMemoryError 指示 JVM 在遇到 OutOfMemoryError 错误时将 heap 转储到物理文件中。HeapDumpPath 表示要写入文件的路径; 可以给出任何文件名; 但是,如果 JVM 在名称中找到一个 <pid> 标记,则当前进程的进程 id 将附加到文件名中,并使用.hprof格式OnOutOfMemoryError 用于发出紧急命令,以便在内存不足的情况下执行; 应该在 cmd args 空间中使用适当的命令。例如,如果我们想在内存不足时重启服务器,我们可以设置参数: -XX:OnOutOfMemoryError="shutdown -r" 。UseGCOverheadLimit 是一种策略,它限制在抛出 OutOfMemory 错误之前在 GC 中花费的 VM 时间的比例
          11. 其他
          12. -server : 启用“ Server Hotspot VM”; 此参数默认用于 64 位 JVM-XX:+UseStringDeduplication : Java 8u20 引入了这个 JVM 参数,通过创建太多相同 String 的实例来减少不必要的内存使用; 这通过将重复 String 值减少为单个全局 char [] 数组来优化堆内存。-XX:+UseLWPSynchronization: 设置基于 LWP (轻量级进程)的同步策略,而不是基于线程的同步。``-XX:LargePageSizeInBytes `: 设置用于 Java 堆的较大页面大小; 它采用 GB/MB/KB 的参数; 页面大小越大,我们可以更好地利用虚拟内存硬件资源; 然而,这可能会导致 PermGen 的空间大小更大,这反过来又会迫使 Java 堆空间的大小减小。-XX:MaxHeapFreeRatio : 设置 GC 后, 堆空闲的最大百分比,以避免收缩。-XX:SurvivorRatio : eden/survivor 空间的比例, 例如-XX:SurvivorRatio=6 设置每个 survivor 和 eden 之间的比例为 1:6。-XX:+UseLargePages : 如果系统支持,则使用大页面内存; 请注意,如果使用这个 JVM 参数,OpenJDK 7 可能会崩溃。-XX:+UseStringCache : 启用 String 池中可用的常用分配字符串的缓存。-XX:+UseCompressedStrings : 对 String 对象使用 byte [] 类型,该类型可以用纯 ASCII 格式表示。-XX:+OptimizeStringConcat : 它尽可能优化字符串串联操作。
  3. Java Web
    1. JDBC
    2. Cookie & Session
    3. jsp
    4. tomcat
    5. servlet
    6. filter & listener
  4. 数据库 & 中间件
    1. MySql
    2. Redis
    3. MongoDB
    4. Oracle
    5. ActiveMQ
    6. RocketMQ
    7. Kafka
    8. RabbitMQ
  5. 搜索引擎
    1. elasticsearch
    2. solr
  6. 系统设计
    1. 常用框架
      1. Spring
      2. SpringMvc
      3. Spring Boot
      4. Mybatis
      5. Hibernate
      6. Dubbo
      7. JPA
      8. Netty
      9. ZooKeeper
    2. 认证授权
      1. Cookie Session
      2. JWT
      3. SSO
      4. OAuth2
    3. 分布式
      1. 理论
        1. CAP理论 & BASE理论
        2. Paxos
        3. Raft算法
      2. RPC
      3. 服务注册与发现
      4. 配置中心
      5. 分布式链路追踪
      6. API网关
      7. 消息队列
      8. 数据库扩展:分裤分表、读写分离
      9. 分布式ID
      10. 分布式接口幂等性
      11. 分布式限流
    4. 微服务
      1. Spring Cloud
    5. 高并发
      1. 消息队列
      2. 读写分离 & 分库分表
      3. 负载均衡
    6. 高可用
      1. 限流 & 降级 & 熔断
      2. 排队
      3. 集群
      4. 超时和重试机制
    7. 运维部署
      1. Docker
      2. Kubernetes
      3. Nginx
    8. 其他
      1. 日志
      2. 监控中心
  7. 其他
    1. 项目:询问你做的项目情况比如遇到了那些问题、用了什么技术
    2. 手撕算法
    3. 源码分析
    4. 设计题:比如给你一个具体的业务场景让你设计解决方案