Linux命令
ls # 列出当前目录下的所有文件和文件夹
pwd #显示当前路径
cd # 切换路径
mkdir # 创建目录
rmdir # 删除空目录
cp # 复制
mv # 改名/移动
touch # 创建空文件
cat # 显示文件内容
more # 显示带分页的内容
less # 显示带分页的内容且可前翻
head # 显示头几行
tail # 显示尾几行
chmod # 更改权限
chown # 更改所有者
Docker
Docker用于创建容器,用于部署服务。
docker images # 查看所有已下载镜像
docker container ls # 查看所有容器
docker ps #查看正在运行的容器
docker search mysql # 查看mysql相关镜像
docker pull mysql:5.7 # 拉取mysql镜像
docker rmi f6509bac4980 # 或者 docker rmi mysql
docker logs -f 289cc00dc5ed # -f 实时
JDK、JRE和JVM
- jdk 包含 jre,jre 包含 jvm。
- 编译需要 jdk,运行需要 jre。
JVM
Java虚拟机,各个系统都有相应的jvm,根据相同的字节码文件编译出相同的结果。
结构
- 方法区:元空间,用的本地内存,更不容易溢出。
- 堆:new的对象都存放在这里,共享的区域。
- 虚拟机栈:线程每调用一次方法,都会存入一个栈帧,栈帧有局部变量表,操作数栈等。
- 本地方法栈:存放了Native服务。
- 程序计数器:存放当前线程执行的位置。
GC
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆。
什么样的才叫垃圾呢?
-
引用计数法
- 每有一个引用,计数器+1
- 引用失效,计数器-1
- 计数器为0时,则代表废弃对象
- 若当两个对象互相引用,则无法回收
-
可达性分析算法:
- 当一个对象没有可到达的任何引用链就会被标记废弃。
- GC Roots可以是
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 本地方法栈(Native 方法)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
垃圾收集算法
- 标记-清除算法
- 标记所有不需要回收的对象,标记后统一回收没有被标记的对象。
- 容易产生大量不连续内存碎片。
- 标记-复制算法
- 用相同大小的两块内存,当一块内存使用完后,将还存活的对象复制到另一块内存去,并将最开始那块内存清空。
- 用了更多的内存。
- 若数据量过大,效率会降低。
- 标记-整理法
- 标记所有不需要回收的对象,标记后让存活对象向一端移动,然后直接清理掉端边界以为的内存。
- 分代收集算法
- 新生代:存活率较低,大量对象死去,可采用
标记-复制
。 - 老年代:存活率较高,可采用
标记-整理
或者标记-清除
。 - 这个算法也是解释了为什么要分新生代和老年代,可以针对不用的阶段使用更有效率的算法。
- 新生代:存活率较低,大量对象死去,可采用
垃圾收集器
JDK1.8默认Parallel Scavenge(新生代)+ Parallel Old(老年代)
JDK 9 ~ JDK20默认 G1
- Serial:单线程,会
Stop The World
,停顿时间过长,但简单高效。 - ParNew:Serial的多线程版,效率更快一些。
- Paraller Scavenge:更注重用户线程的停顿时间,主要是为了提高吞吐量,可以自己设定合适的停顿时间也可以交给收集器自适应去调整。
- CMS(Concurrent Mark Sweep):并发收集器,使用
标记-清除
算法,一共4个阶段。- 初始标记:暂停所有其他线程,记录与GC Roots直接相连的对象,速度很快。
- 并发标记:同时开启GC和用户线程,由于用户线程可能又会不断更新引用域,所以可达性分析不能保证实时性。
- 重新标记: 为了修正并发期间因用户线程运行导致标记变动的那部分对象的标记记录,会产生一个停顿时间,这个时间比初始标记长一点,但是远远比并发标记阶段时间段。
- 并发清除:开启用户线程,同时GC线程开始对未标记的区域清扫。
- G1(Garbage-First) :面向服务器的垃圾收集器,极高概率满足GC停顿时间的同时,也具备高吞吐量的性能特征,分4个步骤。
- 初始标记、并发标记、最终标记、筛选收回。
- 对CPU利用率更高。
- 可设定指定时间m毫秒,停顿时间会极大的靠近这个时间。
- 后台会维护一个优先列表,每次根据允许的收集时间去优先选择回收价值最大的区域。
- ZGC:采用
标记-复制
算法,停顿时间更短,牺牲了一些吞吐量。
设计模式
创建型
工厂
- 简单工厂:工厂类根据传参去生产产品。
- 工厂方法模式:定义一个抽象工厂类,将生产任务交给不同的子类去生产。
- 抽象工厂:要生产别的系列产品,例如苹果和安卓,会在子类去增加新的产品生产方法。
单例
- 饿汉式:线程安全,类加载初始化
- 懒汉式:懒加载,线程分安全和不安全,取决于获取实例的方法是否有加
synchronized
关键字 - 双重检查锁:懒加载,线程安全,用了
volatile
定义变量以及两次非空校验。 - 静态内部类:懒加载,线程安全,静态内部类只会被初始化一次。
- 枚举:简单高效,线程安全,但是枚举类无法被继承,也无法控制加载时机。
结构型
代理
- 用于提供对其他对象的间接方法,在调用对象方法的前后可以进行一些处理操作。分静态代理和动态代理。
- 静态代理与动态代理区别:静态代理事先写好了对应的代理类,被代理对象也是明确的,当被代理类发生变化,代理类也可能需要跟着变化。动态代理是在运行时动态创建代理对象。
JDK代理
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户");
}
}
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务");
Object result = method.invoke(target, args);
System.out.println("提交事务");
return result;
}
}
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(userService);
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler);
proxy.save();
}
}
CGLIB代理
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.Enhancer;
public class CGLIBExample {
public static void main(String[] args) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置目标对象的类
enhancer.setSuperclass(TargetObject.class);
// 设置回调对象
enhancer.setCallback(new MethodInterceptor() {