# Java
:一种编译型语言。
- 通过 JVM 容易实现跨平台运行,因此常用于开发 Web 应用、Android 应用。
# 相关历史
- 1991 年,Sun 公司的 James Gosling 等人开始研发一种适用于单片机系统的编程语言。
- 他们将 C++ 语言进行简化,抛弃了多继承、指针等复杂功能,并提高程序的兼容性。
- 他们将这种语言取名为 Oak ,介绍给硬件厂商,但并没有受到欢迎。
- 1995 年,Sun 公司发现 Oak 语言在互联网上的应用优势:它容易移植到不同平台上运行。于是将它改名为 Java 重新发布,终于成功推广。
- 2010 年,Sun 公司被 Oracle(甲骨文)公司收购。
- 从 2019 年 1 月开始,使用 Java 8 及以上版本的 Oracle JDK 需要付费。因此推荐使用 OpenJDK 。
# 语法特点
- Java 源文件的扩展名为 .java 。需要先编译成扩展名为 .class 的类文件,再交给 JVM 解释执行。
- 每个语句的末尾以分号 ; 作为分隔符。
- 用
//
声明单行注释,用/*
、*/
声明多行注释。 - 支持面向对象编程,但不支持定义函数。
- Java 的语法与 C++ 相似,但是做出了许多优化,比如:
- 丢弃了 C++ 中一些难用的功能,比如操作符重载、多继承。
- 用引用取代了指针,并自动回收垃圾内存。
# 相关概念
- JMX(Java Management Extensions)
- :一组用于监控、管理 Java 应用的 API ,属于 Java 标准库。
- 将被管理的对象称为 MBean(Managed Bean)。
- 基于 RMI(Remote Method Invocation ,远程方法调用)协议进行通信。
- 在 Java 应用中调用 JMX 库,实现其接口,便可以从外部通过 HTTP 通信等方式监控、管理该 Java 应用。
- :一组用于监控、管理 Java 应用的 API ,属于 Java 标准库。
- JAAS(Java Authentication and Authorization Service ,Java 认证和授权服务)
- :一个进行身份认证的 Java 框架,内置在 JRE 中。
- JDBC(Java Database Connectivity)
- :Java 程序访问数据库的 API 规范,主要用于关系型数据库。
- JDBC 通过特定格式的 URL 连接到数据库,配置示例:
datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://10.0.0.1:3306/test?characterEncoding=utf8&connectionCollation=utf8mb4_general_ci&autoReconnect=true&useSSL=false username: root password: ******
- Mybatis
- :一个 Java 的持久化框架。
- 用户不需要直接编写 JDBC 代码,而是先在 XML 文件中编写 SQL 语句,然后通过 Java 方法调用。但可读性差。
- Java 持久化框架还有 Hibernate、JPA 等。
- 开发 Java Web 项目的常用框架:
- SSH(Spring + Struts + Hibernate)
- SSM(Spring + SpringMVC + MyBatis)
- Java 的日志框架
- java.util.logging :Java 的标准库,是 JDK 内置的日志框架。
- Log4j :一个 Apache 开源项目,已停止开发。使用一个配置文件 log4j.properties ,支持自动轮换日志文件。
- LogBack :重新实现了 Log4j ,大幅优化。
- Log4j2 :对 Log4j 进行了重构优化。
- Scala
- :一种基于 Java 的编译型语言,于 2004 年发布。
- 将源代码编译成 Java 字节码,然后由 JVM 运行。
- 支持面向对象编程、函数式编程,属于静态类型语言。
# GC
:垃圾回收(Garbage Collection),指销毁 Java 进程中不需要保留的对象,回收其内存。
Java 进程占用的内存分为几部分:
- Heap :堆内存。主要存放 Java 类的实例对象。
- Direct Memory :直接内存,存放与操作系统交互的数据,比如文件。
- Metaspace :存放 Java 类的元数据,包括常量、注解等。替代了 Java 8 以前的永久代(Permanent)。
- JVM native :运行 JVM 本身占用的内存。
- Code Cache :存放根据 Java 字节码生成的机器代码。
- Thread Stack :线程堆栈。
Java 进程运行时,会从操作系统申请一些内存空间,划分为 Heap、Metaspace 等区域,统称为 committed_memory 。
- 申请的内存空间不一定实际使用、存放了数据。JVM 内部实际使用的内存称为 used_memory ,它小于等于 committed_memory 。
- 在 JVM 外部、操作系统的视角, committed_memory 全部属于 Java 进程占用的内存,会计入主机的 used 内存。
- Java GC 的目的,是在 committed_memory 中减少 used_memory ,从而腾出内存空间来写入新数据。
- 一般不会减少 committed_memory ,也不会将 committed_memory 中的空闲内存释放给操作系统,而是留给 JVM 自己以后使用。因为释放内存、重新申请内存的开销都较大。
对象的引用分为四种,从强到弱如下:
- 强引用(Strong Reference)
- :指向一些必须保留的对象,比如
Object obj = new Object()
。 - 即使内存不足,强引用也不会被 GC 算法回收。
- 强引用是造成内存泄漏的主要原因。
- :指向一些必须保留的对象,比如
- 软引用(Soft Reference)
- :指向一些可能有用,但不是必须保留的对象。
- 当内存不足时,会被 GC 算法回收。
- 弱引用(Weak Reference)
- :指向一些不需要保留的对象。
- 每次 GC 时,都会被回收。
- 虚引用(Phantom Reference)
- :最弱的引用,相当于没有引用。
- 不能用于获取对象的实例,主要用于判断对象是否被垃圾回收。
- 强引用(Strong Reference)
发现垃圾对象的常见算法:
- 引用计数(Reference Counting)
- 原理:
- 每个对象内置一个引用计数器,表示有多少个地方引用了它。当引用数减为 0 时,就可以销毁该对象。
- 创建一个对象并传递给一个变量时,该对象的引用计数为 1 。
- 销毁一个对象时,将它引用的其它所有对象的引用技术减 1 。
- 缺点:
- 修改引用计数的开销较大。
- 难以发现循环引用的对象。比如对象 A、B 相互引用,则可能一直不会被销毁。
- 原理:
- 跟踪(Tracing)
- 又称为可达性分析。
- 原理:
- 选出当前的局部变量、静态变量、线程等对象,作为根集(Root Set)。
- 遍历根集中的各个对象,递归寻找它们引用的对象,记录成一条条引用链。
- 不在引用链上的对象,就可以销毁。
- 目前跟踪算法更常用。
- 引用计数(Reference Counting)
发现垃圾对象之后,常见的 GC 算法:
- 标记-删除算法
- :先标记垃圾对象,然后删除它们。
- 缺点:容易在内存中产生大量碎片空间。
- 标记-整理算法
- :在标记-删除算法之后,增加一个整理阶段,减少内存碎片。
- 缺点:增加了 GC 耗时。
- 复制算法
- :划分两块内存空间。开始一次 GC 时,将前一个空间中的非垃圾对象全部拷贝到后一个空间,然后清空前一个空间。如此循环切换内存空间。
- 这样能减少内存碎片,但是内存利用率只有 50% 。
- 分代收集算法
- :划分两块内存空间,采用不同的 GC 算法:
- 年轻代(young generation):又称为新生代(new generation),用于存放寿命较短的对象,适合复制算法。细分为两种区域:
- 伊甸园空间(eden space):用于存放新创建的对象。
- 幸存者空间(survivor space):用于存放在 young GC 之后幸存、但尚未存入 old 区域的对象。默认有两个 survivor space ,简称为 S0、S1 。
- 老生代(old generation):用于存放寿命较长的对象,适合标记-整理算法。
- 年轻代(young generation):又称为新生代(new generation),用于存放寿命较短的对象,适合复制算法。细分为两种区域:
- 大部分 Java 对象的寿命都较短,很快就没用了,因此采用分代收集算法的效率较高。
- 新创建的对象最初存放在 young 区域的 eden space ,其中大部分会在短期内停止引用而被销毁。
- young GC :当 eden space 内存不足以存入新对象时,会触发一次 GC ,进行以下操作:
- 将 eden space 中被标记引用的对象移到 survivor space 。如果 survivor space 内存不足,则直接移到 old 区域。
- 将 survivor space 中存在时间较长的对象(即活过了多次 young GC)移到 old 区域。
- 按复制算法,交替使用两个 survivor space ,从而减少内存碎片。比如将 S0 中的全部对象复制到 S1 ,然后清空 S0 。
- old GC :当 old 区域的内存不足以存入新对象时,会触发一次 GC ,将存在时间较长的对象删除。
- 发生 young GC 之前、之后,都可能判断出 old 区域内存不足,从而触发 old GC 。
- full GC :对 young、old、Permanent 区域进行 GC ,清理全部堆内存。
- 不同垃圾收集器触发 full GC 的条件不同。
- :划分两块内存空间,采用不同的 GC 算法:
- 标记-删除算法
STW(Stop The World)
- :GC 过程中的一种状态,暂停用户线程,只执行 GC 线程。
- STW 状态是不可避免的,不同的 GC 算法可能 STW 时长不同,越短越好。
- full GC 的 STW 时间通常最长,导致用户线程的明显停顿。
JVM 规范不包含 GC ,但 JVM 通常提供了 GC 功能,常见的几种垃圾收集器:
- Serial GC
- :串行收集。GC 时运行单个 GC 线程,全程为 STW 状态。
- Parallel GC
- :并行收集。GC 时运行多个 GC 线程,全程为 STW 状态。
- 比 Serial 的速度快几倍。
- Concurrent Mark-Sweep(CMS) GC
- :并发收集。GC 时分为多个阶段,部分阶段允许同时运行 GC 线程、用户线程。
- young GC、full GC 全程为 STW 状态,而 old GC 只有部分阶段会 STW 。
- 例如 ElasticSearch 默认采用 CMS GC 算法。
- G1 GC
- :类似 CMS 算法,但能减少内存碎片、控制 STW 时长。
- 传统 GC 算法的 young、old 区域分别是一块地址连续的内存空间。而 G1 GC 在堆内存中划分大量 region ,分别分配给 eden、survivor、old 区域。
- 每个 region 是一小块地址连续的内存空间,体积相同。
- 体积巨大的对象(humongous object)可能占用多个地址连续的 region 。
- 分代 GC 时只需改变 region 所属的内存区域,属于移动式算法,不需要复制 region 。
- G1 GC 有几种模式:
- young GC :清理年轻代。比如将 eden 区域中幸存的 region 分配给 survivor、old 区域。
- mixed GC :清理年轻代,还会清理老生代中垃圾较多(即活动对象较少)的 region ,称为垃圾优先。
- full GC :当老年代内存不足时,清理全部堆内存。
- 例如 Java 8 默认采用 Parallel 算法,Java 9 开始默认采用 G1 算法。
- Serial GC
# 关于 Web
JSP(Java Server Pages)
- :一种动态网页开发技术,可以在 HTML 文件中通过特定的标签嵌入 Java 代码。
- 例:
<p> 今天的日期是: <%= (new java.util.Date()).toLocaleString()%> </p>
Servlet
- :一个提供 Web 应用服务的 Java 程序,本质上是实现了 javax.servlet.Servlet 接口的 Java 类。
- 可以运行在支持 Servlet 规范的 Web 服务器中,比如 Tomcat、Jetty 。
Netty
- :一个 Java 的 Socket 通信框架,支持 TCP、UDP 等协议,或自定义协议。
- 采用非阻塞 IO (NIO),并发能力强。
# spring
Spring Framework
- :一个流行的 Java 应用开发框架,于 2002 年发布。
- 内嵌了一个 Tomcat ,因此可以开发 Web 应用,作为 Web 服务器运行。
- Spring 中的应用程序由一个或多个 bean 对象组成,由 IoC 容器管理。
- 编程时不需要主动 new 对象,而是在代码中声明需要的对象,并在 XML 文件中配置。程序运行时,IoC 容器会去自动根据配置创建对象。
- 对象的控制权从程序员手中,转交给了程序自身,因此称为控制反转(Inversion of Control ,IoC)。
- 例:
@Component // 使用 Component 注解,将该类定义为 Bean 。创建对象时,会根据类名来默认命名(开头小写),比如 phone public class Phone { ... } @Component public class Person { @Autowired // 使用 Autowired 注解,创建对应类型的 Bean 对象并注入该属性 Phone phone; ... }
Spring Boot (opens new window)
- :一个基于 Spring Framework 4.0 的轻量级框架,于 2014 年发布。
- 增加了一些默认配置,简化了开发流程,可以快速开发单个应用。
Spring Cloud
- :一个基于 Spring Boot 的微服务框架,于 2015 年发布。
- 包含多个组件:
- Config :配置管理中心,基于 git 存储配置信息。例:
@Value("${test.name}") // 添加注解,将指定的配置参数赋值给该变量 String name = "Hello";
- Bus :消息总线,用于动态更新配置。
- 每次修改配置时,会自动发送 HTTP 请求通知使用该配置的所有服务,让它们重新从 Config 读取配置。
- Eureka :用于服务发现。
- Ribbon :一个 HTTP 客户端,发出请求时会对服务器进行负载均衡。
- Feign :一个声明式的 Restful 客户端,简化了访问微服务的过程,并基于 Ribbon 进行负载均衡。例:
@FeignClient("service1") // 添加注解,声明要调用的目标服务名 static interface TestService { @GetMapping("/name") // 添加注解,使得调用该方法时,会发送 GET 请求到目标服务的指定 URL public String name(); }
- Hystrix :断路器,自动进行服务熔断。
- Zuul :API 网关,用于将服务暴露到集群外,并进行负载均衡、鉴权。
- Config :配置管理中心,基于 git 存储配置信息。例: