Java与C/C++、Python的区别?

解释型语言是可以一边编译一边执行,但是编译型语言必须是要先全部编译完成,才能执行。
为什么Java代码可以实现一次编写、到处运行?
JVM(Java虚拟机)是Java跨平台的关键。

C/C++为什么不行呢?(不能实现跨平台)
即C语言编译出来的程序无法二进制跨平台, 每个操作系统带的C/C++编译器不同,
C/C++通过编译生成的目标代码,是直接由机器执行的,不同的机器识别的机器指令不同,因而不能跨平台。
在java中表示“静态”的意思,主要用于内存管理。
static关键字可以用在变量、方法、代码块和嵌套类上。这些被static修饰之后,会随着类的加载而加载,具有公共的特性。 static关键字的作用是把类的成员变成类相关,而不是实例相关。
静态(static)修饰如下:
多态就是对象所属的类不同,外部对同一方法的调用执行的逻辑也不同。
特点:

(1)除了可以继承父类的方法外
//展示了继承
public class polymorphism {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.shower();
dog.shower();
}
}
//父类
class Animal{
public void shower(){
System.out.println("宠物洗澡了");
}
}
//子类1
class Dog extends Animal {
}
//子类2
class Cat extends Animal {
}
(2)重写方法:实现外部对同一方法的调用,执行的逻辑不同
public class polymorphism {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.eat();
dog.eat();
}
}
//父类
class Animal{
public void eat(){
System.out.println("宠物吃饭了");
}
}
//子类1
class Dog extends Animal {
@Override
public void eat(){
System.out.println("狗狗吃饭");
}
}
//子类2
class Cat extends Animal {
@Override
public void eat(){
System.out.println("猫猫吃饭");
}
}
注意static不能实现多态:
一,java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏。
二,原因:
ArrayList的扩容机制
ArrayList的底层是用数组来实现的,默认第一次插入元素时创建大小为10的数组,超出限制时会增加50%的容量 (1.5倍扩容),并且数据以 System.arraycopy() 复制到新的数组,因此最好能给出数组大小的预估值。
java 即是编译型又是解释型,.java文件先通过编译器编译成 .class文件 ,再通过jvm的解释器,解释成机器语言让操作系统直接运行。

C/C++/C#等都是编译型语言。 以C语言为例,源代码被编译之后生成中间文件(.o和.obj),然后用链接器和汇编器生成机器码,也就是一系列基本操作的序列,机器码最后被执行生成最终动作。

Lisp/R/Python等都是解释型语言。
其实许多编程语言同时采用编译器与解释器来实现,这就包括Python,Java等,先将代码编译为字节码,在运行时再进行解释。所谓“解释型语言”(Python)并不是不用编译,而只是不需要用户显式去使用编译器得到可执行代码而已 。

显然,采用new的方式会多创建一个对象出来,会占用更多的内存,所以 一般建议使用直接量的方式创建字符串。
不能!不能对布尔值进行转换!(布尔值是按位的)
使用new关键字创建对象的时候:

Step1:类加载检查
虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
Step2:分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。
内存分配的两种方式 :
指针碰撞 :适用场合 :堆内存规整(即没有内存碎片)的情况下。
空闲列表 :适用场合 : 堆内存不规整的情况下。
Step3:初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
Step4:设置对象头
初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
Step5:执行 init 方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始, 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
对象实例化过程,就是执行类构造函数对应在字节码文件中的 < init>() 方法(实例构造器), < init>()方法由非静态变量、非静态代码块以及对应的构造器组成。
静态变量、静态代码块、普通变量、普通代码块、构造器的执行顺序如下图:

具有父类的子类的实例化顺序如下:


从两方面回答:
Java中的集合类主要由Collection和Map这两个接口派生而出,其中Collection接口又派生出三个子接
口,分别是Set、List、Queue。所有的Java集合类,都是Set、List、Queue、Map这四个接口的实现
类,这四个接口将集合分成了四大类,其中:

并发: 多个程序访问同一个对象。
线程同步: 多个操作在同一时间内,只能有一个线程进行,其他的线程要等此线程执行完了之后才可以继续执行。(同步就是协同步调,按预定的先后次序进行运行。)
线程同步的形成条件:队列 + 锁
什么时候需要线程同步?
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改该对象。这时候我们就需要线程同步。
线程同步其实时一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程执行完毕,下一个线程再使用。
简单回答:
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入了锁机制 synchronized / lock ,当一个线程获得对象的锁,独占资源,其他线程必须等待,使用后释放锁即可。
详细回答:
厕所举例:
同一资源相当于厕所位,一个线程拿锁进去,其他线程等待,使用完后释放锁。
synchronized作为关键字,实现线程同步有两种用法:
this;objsynchronized (obj){
}
要实现线程同步我们应该锁变化的量,即需要增删改的对象
死锁概念及产生原理
死锁产生的4个必要条件
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。破坏其中一个或几个条件就可以解决死锁问题。
不能,因为构造方法要和类名相同,如果子类重写父类的构造方法,那么子类将会存在于类名不同的构造方法,这与构造方法的要求是矛盾的。
synchronized可以修饰静态方法,但不能修饰静态代码块。
当修饰静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此静态方法锁相当于该类的一个全局锁。
MySQL之数据库三大范式
什么是范式?
范式就是一种规则约束,数据库范式就是通过约束来优化数据库数据的存储方式,没有规矩不成方圆,没有约束就没有性能卓越的。
最常用的三大范式:
ElasticSearch为何查询速度快(秒懂)
ElasticSearch搜索引擎常见面试题总结
JVM 主要由四大部分组成:类加载器、运行时数据区、执行引擎、本地库接口。

可以作为switch参数数据类型的有:int、bype、short、char、String、枚举(整数、枚举、字符、字符串)
不能作为switch参数的有:long、float、double、boolean、复杂的表达式
String a = "哈哈";
switch(a) {
case "嘻嘻":
System.out.println("星期一");
break;
case "哈哈":
System.out.println("星期二");
break;
case "拉拉":
System.out.println("星期三");
break;
default:
System.out.println("输入有误");
break;
}
输出:
星期二
int day = 6;
switch(day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期日");
break;
default:
System.out.println("输入有误");
break;
}
输出:
星期六
Integer day = new Integer(6);
switch(day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期日");
break;
default:
System.out.println("输入有误");
break;
}
输出:
星期六
爆红
斐波那契数列:1,1,2,3,5,8,11,13…
f(n)
=f(n-1)+f(n-2)
=f(n-2)+f(n-3)+f(n-3)+f(n-4)
=…
int fibonacci(int n)
{
if (n <= 2)
return 1;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}
斐波那契数列:1,1,2,3,5,8,11,13…
f(3)=f(2)+f(1)
f(4)=f(3)+f(2)
f(5)=f(4)+f(3)
…
int fibonacci(int n)
{
if (n <= 2)
return 1;
int first = 1, second = 1, answer;
for (int i = 3; i <= n; i++)
{
answer = first + second;
first = second;
second = answer;
}
return answer;
}
斐波那契数列:1,1,2,3,5,8,11,13…
int fibonacci(int n)
{
vector <int> dp;
dp.push_back(0);
dp.push_back(1);
for (int i = 3; i <= n; i++)
dp.push_back(dp[i-1] + dp[i-2]);
return dp[n];
}
hashmap是2倍扩容,ArrayList是1.5倍扩容。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
Spring Boot作为Spring的脚手架框架,其设计目的是用来简化Spring应用得初始搭建以及开发过程,以达到快速构建项目、预置三方配置、开箱即用的目的。Spring Boot有如下的优点:
parent(spring-boot-starter-parent)让我们写依赖的时候不需要写版本号,极大地避免了版本冲突问题(依赖管理,不是依赖)
| 类 / 配置文件 | Spring | SpringBoot |
|---|---|---|
| pom文件中的坐标 | 手工添加 | 勾选添加(创建项目时) |
| web3.0配置类 | 手工制作 | 无 |
| Spring/SpringMVC配置类 | 手工制作 | 无 |
| 控制器 | 手工制作 | 手工制作 |
思路总结:主要考虑数据库优化与SQL语句优化。
Redis缓存穿透、缓存击穿缓存雪崩1
Redis缓存穿透、缓存击穿缓存雪崩2

当有大量查询请求未命中缓存时,引起对后台数据库的频繁访问,导致数据库负载压力增大,这种现象就叫做缓存穿透。
引起的原因:
大量访问不存在的key,导致数据库处理大量请求。(访问缓存,缓存没有就会访问数据库)

解决方法:
当Redis中存在某些极热点数据时,即有大量请求并发访问的key-value数据。当极热点key-value数据突然失效时,缓存未命中引起对后台数据库的频繁访问,这种现象叫缓存击穿。
引起的原因
缓存上极热点数据突然失效 (点)

解决方法
当某⼀时刻发⽣⼤规模的缓存失效的情况,例如缓存服务宕机、大量key在同一时间过期,这样的后果就是⼤量的请求进来直接打到DB上,可能导致整个系统的崩溃,称为雪崩。如果运维重启宕机的数据库,马上又会有大量新的请求流量到来,再次引起数据库宕机。
可能的原因

解决方法
3. 引入随机性,在原有缓存失效时间上加上一个随机值,避免大量数据在同一时间失效。
4. 通过请求限流、熔断机制、服务降级等手段,降低服务器负载。
5. 实现缓存组件的高可用,防止单点故障、机器故障、机房宕机等一系列问题。
6. 提高数据后台数据库的容灾能力。
SpringBean默认是单例的,高并发情况下,如何保证并发安全
Spring官方提供的bean,一般提供了通过 ThreadLocal 去解决线程安全的方法。
Java 并发机制实现 原子操作 有两种: 一种是锁,一种是CAS。 (无锁的CAS操作在性能上要比同步锁高很多。)
CAS实际是普遍处理器都支持的一条指令,这条指令通过判断当前内存值V、旧的预期值A、即将更新的值B是否相等来对比并设置新值,从而实现变量的原子性。
CAS的原理: 通过三个参数,当前内存的变量值V、旧的预期值A、即将更新的值B。通过判断是V和A是否相等查看当前变量值是否被其他线程改变,如果相等则变量没有被其他线程更改,就把B值赋予V;如果不相等则做自旋操作(自旋就是不断尝试CAS操作,通常设置自旋次数防止死循环)。
悲观锁:synchronized
乐观锁:CAS (虽然叫锁,但它实际上并没有用到锁,而是一种无锁的同步机制)
CAS的缺点主要有3点:
| MYISAM | INNODB | |
|---|---|---|
| 事务支持 | 不支持 | 支持 |
| 数据行锁定 | 不支持 | 支持 |
| 外键约束 | 不支持 | 支持 |
| 全文索引 | 支持 | 不支持 |
| 表空间的大小 | 较小 | 较大,约为MYISAM的2倍 |
常规使用操作:
MYISAM 节约空间,速度较快
INNODB 安全性高,事务的处理,多表多用户操作
重载: 发生了 同一类 中,若多个方法之间 方法名相同,参数列表不同,则他们构成重载关系。重载与方法的返回值以及访问修饰符无关,即 重载的方法不能根据返回类型进行区分。
重写: 发生在 父类子类 中,子类想重写父类的方法,那 它的方法名、参数列表必须与父类方法相同。另外,返回值要小于等于父类方法,抛出的异常要小于等于父类方法,访问修饰符则要大于等于父类方法。还有,若父类方法的访问修饰符为private,则子类不能对其重写。
相同:
区别:
注意: 由于接口定义的是一种规范,因此 接口里不能包含构造器和初始化块定义。
接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法或私有方法)、内部类(包括内部接口、枚举)定义。

处理异常的语句由try、catch、finally三部分组成。
try块用于包裹业务代码,catch块用于捕获并处理某个类型的异常,finally块则用于回收资源。
在Java中,可以按照如下三个步骤处理异常:
final关键字可以修饰类、方法、变量,以下是final修饰这3种目标时表现出的特征:
java 泛型全解 - 绝对最详细
以前集合里的对象都是Object类型,从Java 5开始,Java引入了“参数化类型”的概念,允许程序在创建集合时指定集合元素的类型,Java的参数化类型被称为泛型(Generic)。例如 List ,表明该List只能保存字符串类型的对象。
Java泛型的编译问题
public BoundedEcho
具体来说,通过反射机制,我们可以实现如下的操作:
从Java5开始,Java在java.util.concurrent包下提供了大量支持高效并发访问的集合类,它们既能包装
良好的访问性能,有能包装线程安全。这些集合类可以分为两部分,它们的特征如下:
java.util.concurrent包下线程安全的集合类的体系结构:

红黑树是平衡二叉树,B树是平衡多叉树。
B树的节点可以存储多个数据,当数据量不够多时,数据都会”挤在“一个节点中,查询效率会退化为链表。
HashMap线程不安全,ConcurrentHashMap线程安全
1.7版本
1.8版本
1.7版本
1.8版本
CopyOnWriteArrayList
CopyOnWriteArrayList是Java并发包里提供的并发类,简单来说它就是一个线程安全且读操作无锁的ArrayList。正如其名字一样,在写操作时会复制一份新的List,在新的List上完成写操作,然后再将原引用指向新的List。这样就保证了写操作的线程安全。(上锁的写操作不会影响到并发访问的读操作:写备份,加锁;读原份,不加锁)
优点:读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。在遍历传统的List时,若中途有别的线程对其进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的List容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了。
缺点:一是内存占用问题,毕竟每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC。二是无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。
--------------------------------------------------------四种创建线程方法对比-------------------------------------------------------
实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:
注:在前三种中一般推荐采用实现接口的方式来创建多线程
线程通常有五种状态,创建,就绪,运⾏、阻塞和死亡状态。
线程5种状态的转换关系,如下图所示:

当发生如下情况时,线程将会进入阻塞状态:
针对上面几种情况,当发生如下特定的情况时可以解除上面的阻塞,让该线程重新进入就绪状态:
为了提高资源的利用率。原来频繁地创建和释放线程对象会消耗系统资源,有了线程池就可以通过重复利用已创建的线程来降低线程创建和销毁造成的消耗。
创建多线程,我们要先了解 Executors 这是线程池的工具类,通过这个线程池工具类来创建线程池。创建出来的线程池,都是通过ThreadPoolExecutor类来实现的。

//创建一个线程池,线程池根据需要创建线程,可扩容;任务多线程多,任务少线程少
ExecutorService threadPool = Executors.newCachedThreadPool();
//创建一个线程池,一池有固定线程数,适合于创建长期任务性能好
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//创建一个线程池,里面只有一个线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //多余的空闲线程存活时间
TimeUnit unit, //上面超时参数的单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程创建工厂
RejectedExecutionHandler handler) //拒绝策略
下面,我通过创建一个线程的过程来将这七个参数串起来讲解

在进来的任务数量大于maximumPoolSize + workQueue时,就会触发拒绝策略,线程池默认使用的是AbortPolicy拒绝策略。
在Java中线程通信主要有以下三种方式:
当一个变量被定义成volatile之后,它将具备两项特性:(在JVM底层volatile是采用“内存屏障”来实现的。)
注意,volatile不能保证原子性。 volatile变量在各个线程的工作内存中是存在一致性问题的,但是Java里面的运算操作符并非原子操作,这导致volatile变量的运算在并发下一样是不安全的。
抽象队列同步器 AbstractQueuedSynchronizer (AQS)原理概览
AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

AQS是用来构建锁或者其他同步组件的骨架类,减少了各功能组件实现的代码量,也解决了在实现同步器时涉及的大量细节问题,例如等待线程采用FIFO队列操作的顺序。在不同的同步器中还可以定义一些灵活的标准来判断某个线程是应该通过还是等待。
AQS采用模板方法模式,在内部维护了n多的模板的方法的基础上,子类只需要实现特定的几个方法(不是抽象方法!),就可以实现子类自己的需求。
基于AQS实现的组件,诸如:
说一下公平锁和非公平锁的区别?
公平锁:多个线程按照申请锁的顺序执行,先来后到。
非公平锁:多个线程不按照申请锁的顺序来,有可能先到后得。当非公平锁失败后才会采用公平锁。
ThreadLocal对象可以提供线程局部变量,每个线程Thread拥有一份自己的副本变量,多个线程互不干扰。
ThreadLocal顾名思义是线程私有的局部变量存储容器,可以理解成每个线程都有自己专属的存储容器,它用来存储线程私有变量,其实它只是一个外壳,内部真正存取是一个Map。每个线程可以通过set() 和 get() 存取变量,多线程间无法访问各自的局部变量,相当于在每个线程间建立了一个隔板。只要线程处于活动状态,它所对应的ThreadLocal实例就是可访问的,线程被终止后,它的所有实例将被垃圾收集。总之记住一句话:ThreadLocal存储的变量属于当前线程。
应用场景
ThreadLocal经典的使用场景是为每个线程分配一个 JDBC 连接 Connection,这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的Connection。 另外ThreadLocal还经常用于管理Session会话,将Session保存在ThreadLocal中,使线程处理多次处理会话时始终是同一个Session。
Java 源代码文件经过 Java 编译器编译成字节码文件后,通过类加载器加载到内存中,才能被实例化,然后到 Java 虚拟机中解释执行,最后通过操作系统操作 CPU 执行获取结果。 如下图:

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作“非堆”(Non-Heap),目的是与Java堆区分开来。
Java虚拟机栈(Java Virtual Machine Stack): 每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表 存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
类加载过程详解
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历:
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading) 七个阶段,其中验证、准备、解析三个部分统称为连接(Linking)。
这七个阶段的发生顺序如下图所示:

在上述七个阶段中,包括了类加载的全过程:加载、验证、准备、解析和初始化这五个阶段。
JVM 中内置了三个重要的类加载器,除了 BootstrapClassLoader (启动类加载器)其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader:

在类加载的时候,系统会首先判断当前类是否被加载过。
已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派给父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。
private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,检查请求的类是否已经被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理
c = parent.loadClass(name, false);
} else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//抛出异常说明父类加载器无法完成加载请求
}
if (c == null) {
long t1 = System.nanoTime();
//自己尝试加载
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
避免重复加载 + 避免核心类篡改–Xms和 -Xmx-Xms[unit] -Xmx[unit] (Young Generation)-XX:NewSize和-XX:MaxNewSize指定-Xmn[unit] 指定-XX:NewRatio=来设置新生代和老年代内存的比值-XX:MetaspaceSize=N-XX:MaxMetaspaceSize=N-XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseParNewGC-XX:+UseG1GC-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=< number of log files > -XX:GCLogFileSize=< file size >[ unit ]-Xloggc:/path/to/gc.log引用计数算法
给对象中添加一个引用计数器:
优点: 实现简单,效率高
缺点: 目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
可达性分析算法:
是通过一系列称为 “GC Roots” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

标记:都是标记不需要回收的
标记-清除算法
标记不需要回收的对象,回收掉没有被标记的对象。(下图,蓝色是标记的)

标记-复制算法
将内存分为大小相同的两块,当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。

标记-整理算法
让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法
将 java 堆分为新生代和老年代:
"标记-复制" 算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。“标记-清除”或“标记-整理” 算法进行垃圾收集。Serial 收集器
是单线程收集器,它只会使用一条垃圾收集线程去完成垃圾收集工作,且它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。

ParNew 收集器
除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。

Parallel Scavenge 收集器
使用标记-复制算法的多线程收集器,它看上去几乎和 ParNew 都一样。

Serial Old 收集器
Parallel Old 收集器
CMS 收集器(Concurrent Mark Sweep)
目标: 获取最短回收停顿时间。
整个过程分为四个步骤:
初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;
并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。

G1 收集器
ZGC 收集器
空间复杂度:O(log2n);
平均时间复杂度:O(nlogn)
快排是采用分治递归的思想去进行排序数据
待排序数组中随机找出一个数,可以随机取,也可以取固定位置,一般是取第一个或最后一个称为基准,然后就是比基准小的在左边,比基准大的放到右边。
步骤就是: 先从右往左找一个小于基准的数,再从左往右找一个大于基准的数,然后交换他们,直到碰头结束,碰头位置的数和基准进行交换,这样交换完左边都是比基准小的,右边都是比较基准大的,这样就将一个数组分成了两个子数组,然后再按照同样的方法把子数组再分成更小的子数组,直到不能分解为止。
MySQL主从复制是一个异步的复制过程,主库发送更新事件到从库,从库读取更新记录,并执行更新记录,使得从库的内容与主库保持一致。
为什么要做主从复制?