Java知识树建立

609 阅读10分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战


工作一段时间了,梳理一下自己的知识树。目录顺序围绕部署的Java应用开始,自底向上编写,延展知识树广度,横向延展为深度。准备每个技术写一篇文章。

计算机硬件结构

Java应用部署需要操作系统,操作系统的运行需要CPU,内存,磁盘,网卡。

CPU

作用:从内存中读取程序指令,进行执行。 发展:CPU发展的最终目标是更高的执行速度。就是购买电脑时候运行频率。同时由于CPU的运算速度级别较高,常见的从磁盘读取程序已经跟不上CPU的执行速度,所以程序运行前必须将指令从磁盘读取到内存,CPU在直接从内存中读取指令执行。CPU提高频率的方式有高速缓存(三级高速缓存),多核CPU。

内存

作用:保存CPU执行的指令,保存程序运产生的临时数据。 发展:内存发展的最终目的是为了更高的存储空间,更快的IO速度。更高的存储空间也需要CPU支持更大的寻址空间。

磁盘

作用:持久化存储数据。 发展:磁盘发展的最终目标是更快的磁盘IO速度,更长时间的保存数据(防止数据丢失)。目前我所接触过的就是机械硬盘,固态硬盘。两者相比而言,机械硬盘速度慢(机械硬盘存储方式磁头柱面等物理方式,而固态硬盘使用的是集成电路的方式,速度不在一个级别),但存储时间长(断电情况下机械硬盘能很长时间不丢失数据),读写次数高,而固态硬盘读写速度快,但断电情况下容易丢失数据,并且读写次数少。 基于这个特征,个人感觉互联网行业数据库服务还是会选择机械硬盘。但看到很多持久化相关软件都会去支持固态硬盘的特性(用固态硬盘的例子还求大佬们解惑,存在什么问题)。固态硬盘适合存放少写的数据,比如存放操作系统,这样开机会更快。

网卡

作用:接受发送网络数据包。 发展:网卡的发展的最终目的是更快的网络传输速度。比如XXXMB/s。通常应用的网络流量比较小,但当并发高或者网关类应用需要注意网卡的传输大小。

操作系统

Java应用运行在操作系统之上,操作系统能够分配给Java应用提供内存资源、CPU资源、网卡资源、磁盘资源等等。通常服务端使用的Linux系统,基于此来进行分析。

进程

进程是操作系统分配资源的最小单位,进程运行时,操作系统为其分配堆,栈内存空间。进程中的线程是操作系统调度的最小单位。

CPU虚拟化

操作系统使用分时的方式实现多个任务同时执行,其中关键是操作系统可以通过硬件的中断来跳转到对应中断程序以此重新获取CPU执行权,实现上下文的切换,CPU虚拟化的核心在于调度算法其目标是为了提高CPU的利用率。常见的调度算法有FIFO,先到达的先执行。

内存虚拟化

操作系统采用空间分割的方式为进程分配内存。内存虚拟化的目标是为了隔离不同进程之间内存访问,提供一个虚拟化假象,进程以为自己占用整个内存空间。常见实现方式是基址法,操作系统为每个进程使用的内存空间分配大小,然后记录每个进程的基址,当进程读写内存时候需要加上基址计算出实际的物理地址,由于进程的堆,栈内存可能会增长,所以将堆栈分别安排在进程空间的两边,向中间方向增加内存。

文件系统

文件系统已经是操作系统必不可少的部分,基于磁盘持久化结构构建目录与文件的抽象来存储数据。文件系统中最重要的莫过于如何组织文件系统的数据结构。一个最简单的文件系统由三部分组成:

  • 元数据区:存储元数据信息,比如磁盘大小,inode区起始地址,数据区其实地址等
  • inode区:存储文件信息,目录信息(目录也是文件的一种,区别在于目录的数据存储的是文件名称和路径)。存储文件在数据区的起始地址。
  • 数据区:存储文件数据,当文件大小扩增时,在文件的末尾增加链表结构指向下一块数据区。

shell命令

Linux中基于操作系统提供的命令可以执行一些命令来运行一些指令,以下以Java开发者的视角列举一些命令(也是我开发中常用的一些命令):

查询日志

日志维护项目中,会去查询Nginx日志,Tomcat日志,应用中打印的日志,出现bug时有下面三个命令就够了。 cat 通常用来查询请求用的,cat 【xxx.log】 |grep【traceId】查询一个请求的所有AOP日志来判断问题。 less 项目有的接口一个AOP日志特别多,用cat查询会弹出特别多的日志,所以一般会用less然后加上/【traceId】方式进行查询 awk 针对耗时接口排查,需要根据接口调用的方法耗时定位到哪个方法需要优化。但由于日志太多,cat打印出来不能一眼看出哪个方法耗时高。所以需要对AOP打印的方法名称和耗时进行排序找到最高耗时。awk可以通过分割符号提取出对应的信息进行排序。

但是通过运行命令来查询日志的方式仅适用于部署少量机器的情况下,比如只有10台机器以下,当部署的机器达到几十、上百命令查询的方式也太low了。通常公司内会搭建日志系统,比如kinbana、loki来查询日志。

操作系统信息查询

当性能问题出现时,都需要去查询相关的操作系统信息。比如CPU高、没内存、没磁盘、网络等问题。 排查CPU过高 当CPU过高时,对于我们开发者来说,最关键的信息是知道执行哪什么程序哪行代码导致CPU过高。top可以列举出占用操作系统CPU资源的topN进程。获取到占用最高的进程pid,再通过top -H -p 【进程pid】获取进程下占用CPU最高的线程。随后可以通过jstack过滤处占用CPU最高线程在执行的代码堆栈。

排查内存过高 对于操作系统内存的问题基本没有遇到过,仅仅记得一个top命令可以查看总内存占用与使用信息。但我想排查的基本思路是先获取哪个进程占用内存最多,在定位到内存中存储最多的数据是什么,在结合数据与进程的代码逻辑判断出问题所在。(命令后续补充)

shell脚本编写

有时候单个的命令通常无法定位到问题,或者单个命令只能获取到一丢丢信息。此时需要多个命令组合为shell脚本执行。一个常见的脚本是监控某个时刻CPU最高的堆栈。上面提到的CPU过高只能排查CPU一直高的情况,让你有时间去敲打命令,实际情况是CPU只在某个时间飙升。此时就需要来跑脚本了。

Docker

云原生已经是大势所趋,越来越多的公司、越来越多的中间件已经在支持云原生相关功能。我们开发的Java应用也不再仅仅部署于操作系统之上,而是部署在Docker之上了。说实话,时间开发中Dockerfile也不是开发写的,一般都是k8s运维创建几个Java通用的镜像,部署时候选择就行,但是一些基本的Docker操作还是要会的。

镜像相关

  • docker image: 查看镜像
  • docker pull 【image name】:拉取镜像
  • docker image rm 【image name】:删除镜像

容器相关

  • docker run 【image name】:运行一个镜像容器
  • docker run -it xxxx:进入容器。(这个常用,查日志都是进入容器查日志)
  • docker sstop xxxx:停止运行的容器

DockerFile相关

DockerFile文件用来构建一个镜像。支持Linux自带的一些命令,如果要使用其他命令需要先下载或者上传命令的执行文件到镜像中,除了这些命令之外,Docker提供了一些特殊命令。

  • extend 【image name】继承自哪个镜像,通常会选择原始的Linux操作系统
  • copy 【文件路径】构建时将文件路径的文件添加进镜像中
  • add 【文件路径】需要事先将文件路径放在与DockerFile同级的位置。在构建镜像会将文件添加进容器中,与copy不同之初在于,如何文件是一个压缩文件,比如gz,会将文件进行解压缩
  • cmd 【Linux 命令】在构建时候运行命令
  • run 【Linux 命令】在构建时运行命令,与cmd不同在于run可以接收启动镜像容器的参数,更加灵活

JVM

Java应用想要跑起来,离不开操作系统中运行的JVM进程,JVM要做的事情是:

  1. 跨平台
  2. 优化跨平台带来的性能损耗(jit、更强gc算法)

自动内存管理

JVM对内存结构的定义,垃圾回收

JVM信息查询

对JVM内存信息的查询

cmd命令

图形化界面

虚拟机执行子系统

JVM如何执行一个.class文件,以及其实现方法调用的方式(基于栈的指令)

编译优化

在JVM编译、运行都有着自动优化措施。

前端编译

在编译期可以通过hook做一些事情,比如lombok生成get、set方法。jvm会进行代码优化

后端编译

在运行期可以将热点方法直接生成机器码执行,不需要在将字节码转为机器码。 针对如何发现热点方法,提出了分层编译的思路,以及栈上分配内存的构想。

Java基础

Java八种基本数据类型

  • int
  • short
  • byte
  • long
  • char
  • float
  • dubbo
  • Object

Java面向对象

封装

方法重载

继承

方法重写

多态

Java IO

文件IO

网络IO

Java并发

并发编程的挑战

内存模型带来的可见性问题

解决可见性问题带来的有序性问题

CPU资源受限

IO受限

Java内存模型

通过硬件抽象出的Java内存模型

final语

线程

synchronize

AQS

并发容器

HTTP

请求响应模型

HTTP协议定义

cookie

session

Content-Type定义

JavaWeb开发

Servlet

ServletRequest

ServletResponse

Filter

Intercept

Tomcat

Spring

提供对象的注入与拦截,方便开发。

ICO

AOP

SpringMVC

基于Servlet实现模型-视图-控制分离的MVC架构模式。

MVC三层架构

restful

SpringBoot

基于SpringMVC实现更快速的开发集成组件,体现处boot,执行流程与factory文件密不可分。

代码设计

设计模式

灰度

复杂代码优化

redis

MySQL

MongoDB

RocketMQ

分布式

当应用部署多节点,一切变得不在一样

分布式限流

RPC

服务注册与发现

分布式事物

分布式定时任务

熔断

配置中心

负载均衡

网关

分库分表

接入层

CDN

SSL/TSL

高可用设计