java Object中的方法有哪些
  1. notify()

    随机唤醒其中一个在等待中的线程

  2. wait()

    wait方法会一直阻塞,直到其他线程调用当前对象的notify()/notifyAll()方法将其唤醒;而wait(long)是等待给定超时时间内(单位毫秒),如果还没有调用notify()/nofiyAll()会自动唤醒;waite(long,int)如果第二个参数大于0并且小于999999,则第一个参数+1作为超时时间;

  3. wait(timeout)

  4. wait(timeout,naous)

  5. notifyAll()

    唤醒所有之前在当前对象上调用wait方法的线程

  6. clone()

    此方法返回当前对象的一个副本。 浅拷贝

    浅拷贝:拷贝的是引用。

    深拷贝:新开辟内存空间,进行值拷贝。

  7. toString()

  8. hashCode()

    这是一个public的方法,所以 子类可以重写 它。这个方法返回当前对象的hashCode值,这个值是一个整数范围内的(-2^31 ~ 2^31 - 1)数字。

    对于hashCode有以下几点约束

    • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改;
    • 如果两个对象 x.equals(y) 方法返回true,则x、y这两个对象的hashCode必须相等。
    • 如果两个对象x.equals(y) 方法返回false,则x、y这两个对象的hashCode可以相等也可以不等。 但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
    • 默认的hashCode是将内存地址转换为的hash值,重写过后就是自定义的计算方式;也可以通过System.identityHashCode(Object)来返回原本的hashCode。
  9. equals()

    比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。

  10. getClass()

    获取这个对象的class对象

  11. finalize()

    此方法是在垃圾回收之前,JVM会调用此方法来清理资源。此方法可能会将对象重新置为可达状态,导致JVM无法进行垃圾回收。

    GcRoot的种类

    1.虚拟机栈:栈帧中的本地变量表引用的对象

    2.native方法引用的对象

    3.方法区中的静态变量和常量引用的对象

fail-fast

​ 线程java集合类在用迭代器遍历对象的时候,如果发现集合里的元素被其他线程修改了,马上就会抛出异常。

fail-safe ( 安全失败 )
fail-safe:这种遍历基于容器的一个克隆。因此,对容器内容的修改不影响遍历。java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。常见的的使用fail-safe方式遍历的容器有ConcerrentHashMap和CopyOnWriteArrayList等。

原理:

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

mysql索引

1.普通索引 index :加速查找
2.唯一索引
主键索引:primary key :加速查找+约束(不为空且唯一)
唯一索引:unique:加速查找+约束 (唯一)
3.联合索引
-primary key(id,name):联合主键索引
-unique(id,name):联合唯一索引
-index(id,name):联合普通索引
4.全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。
5.空间索引spatial :了解就好,几乎不用

索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能。

b+树好处:

(1) B+树的磁盘读写代价更低

​ B+的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B树更小。如果把所有同一内部 结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的 关键字也就越多。相对来说IO读写次数也就降低了。

​ (2)B+树的数据信息遍历更加方便

​ B+树只要遍历叶子节点就可以实现整棵树的遍历,而B树不支持这样的操作(或者说效率太低),而且 在数据库中基于范围的查询是非常频繁的,所以数据库索引基本采用B+树

​ (3) B+树的查询效率更加稳定

数据库事务的隔离级别:

  1. 读未提交 没用

  2. 提交读 解决脏读的问题

  3. 可重复读 解决不可重复读问题

  4. 串行化 上锁 。啥都解决

session cookie 区别
  1. session存储在服务端,cookie存储在浏览器端
  2. session大小限制取决于服务端内存,cookie大小一般限制为4k
  3. cookie不安全,别人可以直接看到
osi七层模型

tcp/ip五层:应用层、传输层、网络层、链路层
osi七层模型:应用层、表示层、会话层、传输层、网络层、链路层、物理层

单例模式
//懒汉式
public class Singleton {private static Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {singleton = new Singleton();}return singleton;}
}
//饿汉式
class Singleton {private static Singleton singleton=new Singleton();private Singleton() {}public static Singleton getInstance() {return singleton;}
}
DCL
class Singleton {private volatile static Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}
//利用类初始化上锁的机制实现线程安全
class Singleton {private Singleton() {}private static class Instance {private final static Singleton singleton = new Singleton();}public static Singleton getInstance() {return Instance.singleton;}
}
索引失效情况
  1. 范围查询
  2. 对于组合索引,没有使用第一部分。(未遵循最左匹配原则,最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。)
  3. 查询中进行计算操作
  4. 类型没写对,比如一个判断条件,要是字符串的,你没加单引号
接口与抽象类的区别

接口和抽象类的区别:
含有 abstract 修饰符 class 即为抽象类。

接口中的所有方法都必须是抽象的,接口中的方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final。

抽象类中可以有普通成员变量;接口中没有普通成员变量。

抽象类中可以包含非抽象普通方法;接口中的所有方法必须都是抽象的,不能有非抽象的方法。

抽象类中的抽象方法的访问权限可以是 public、protected 和(默认类型,虽然 eclipse 不报错,但也不能用,默认类型子类不能继承);接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型。

一个类可以实现多个接口,用逗号隔开,但只能继承一个抽象类;接口不可以实现接口,但可以继承接口,并且可以继承多个接口,用逗号隔开。

数据库三范式
  1. 第一范式:不能有表中表
  2. 第二范式:每个表要有主键
  3. 第三范式:无传递依赖,也就是无冗余
ArrayList和LinkedList的区别
  1. arraylist是基于数组实现的,linkedlist是基于双向链表实现的。
  2. arraylist查找效率高,linkelist插入、删除操作效率高。
  3. 这两都是线程不安全的。
Errhor与Exception的区别

首先,Error类和Exception类都是继承Throwable类

Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

where和on的区别

where是生成临时表的条件,on是临时表生成后筛选数据的条件

Public default private protect修饰符的作用域

1.public 在那个地方都能访问
2.default 包内的类可以访问
3.private 本类中才能访问
4.protected 类本身的方法及子类访问,即使子类在不同的包中也可以访问。

什么是聚簇索引

聚簇索引是包含所有的字段的,非聚簇索引是只包含索引本身和主键。

get、post请求区别
  1. GET把参数包含在URL中,POST通过request body传递参数

  2. GET请求会被浏览器主动cache,而POST不会,除非手动设置。

  3. GET请求在URL中传送的参数是有长度限制的,而POST么有。

  4. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

  5. GET请求只能进行url编码,而POST支持多种编码方式。

  6. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

  7. 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

  8. GET参数通过URL传递,POST放在Request body中。

  9. GET在浏览器回退时是无害的,而POST会再次提交请求。

  10. GET产生的URL地址可以被Bookmark,而POST不可以。

io流设计模式

装饰器模式、适配器模式

sleep会不会释放锁,哪个会释放锁。

sleep不释放锁,wait会释放锁

select 中 in 和 exist 有什么区别
tcp、udp区别
  1. tcp面向连接,udp面向无连接的
  2. tcp是面向流的,udp是面向数据报的
  3. tcp比udp的可靠性更强
  4. tcp是一对一的,udp是一对多的
  5. tcp对系统资源要求较高
请求url过程

genarrnkey

1、浏览器搜索自己的DNS缓存,缓存中维护了一张域名和IP地址的对应表

2、若没有则搜索操作系统的DNS缓存

3、若没有则操作系统将域名发送到本地域名服务器,本地域名服务器在自己的DNS缓存查找(递归查询)

4、若没有则通过以下方式查找(递归查询/迭代查询)

本地域名服务器向根域名服务器发送请求,根域名服务器返回com域顶级服务器域名地址

本地域名服务器向com域顶级域名发送请求,com域顶级域名服务器返回权限域名服务器地址

本地域名服务器向权限域名服务器发送请求,得到IP地址

5、本地域名将得到的IP地址返回操作系统,同时自己也将IP地址缓存起来

6、操作系统得到IP地址返回给浏览器,同时自己也将IP地址缓存起来

7、浏览器得到IP地址

用户态和内核态

内核态(Kernel Mode):运行操作系统程序,操作硬件

用户态(User Mode):运行用户程序

线程和进程的区别

程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。

包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

静态代理和动态代理的区别

1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

线程池

线程池7个参数:
corePoolSize:核心线程数量,会一直存在,除非allowCoreThreadTimeOut设置为true
maximumPoolSize:线程池允许的最大线程数量
keepAliveTime:线程数量超过corePoolSize,空闲线程的最大超时时间
unit:超时时间的单位
workQueue:工作队列,保存未执行的Runnable 任务
threadFactory:创建线程的工厂类
handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

4大拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

hashmap,hashtable,concurrentmap,treemap

hashmap键、值均可为空

hashtable键、值均不可为空

concurrentmap键、值均不可为空

treemap键不能为空,值可为空

@Autowire @Resource

@Autowire是按类型注入的,@Resource是按bean、名称注入的,

TCP为什么三次握手不能两次握手、四次握手

  • 为了实现可靠数据传输,TCP协议的通信双方都必须维护一个序列号,以标识发出去的数据包中,那些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号的起始值,并确认对方已经收到了序列号起始值的必经步骤

  • 如果只是两次握手,至多只有连接发起方的起始序列号被确认,另一方选择的序列号则得不到确认。

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)

  • 三次握手才可以同步双方的初始序列号

  • 三次握手才可以避免资源浪费

TCP为什么四次挥手

  1. 第一次挥手 因为当主动方发送断开连接请求(即FIN报文)给被动方时,仅仅代表主动方不会再发送数据报文了,但主动方仍可以接收数据报文。

  2. 第二次挥手 被动方此时有可能还有相应的数据报文需要发送,因此需要先发送ACK报文告知主动方“我知道你想断开连接的请求了”。这样主动方便不会因为没有收到应答而继续发送断开连接的请求(即FIN报文)

  3. 第三次挥手 被动方在处理完数据报文后,边发送给主动方FIN报文,这样可以保证数据通信正常可靠地完成,发送完FIN报文后,被动方进入LAST_ACK阶段(超时等待)

  4. 第四次挥手 如果主动方即使发送ACK报文进行连接中断的确认,这是被动方就直接释放连接,进入可用状态。

为什么TIME_WAIT要2MSL

虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

建立http连接的过程

1、建立TCP连接

2、Web浏览器向Web服务器发送请求命令

3、Web浏览器发送请求头信息

4、Web服务器应答

5、Web服务器发送应答头信息

6、Web服务器向Web浏览器发送数据

7、Web服务器关闭TCP连接

URL,URI区别

http://localhost:8080/myweb/hello.html

http://localhost:8080/myweb/hello.html 这个为URL

myweb/hello.html这个为URI

springboot自动配置

ArrayList的扩容机制

ArrayList是懒加载的,如果在构造方法中未指定初始容量,则在构造这个对象的过程中不会初始化ArrayList数组的长度,在进行add()操作的时候才会指定数组的长度。add()方法在数组为空的情况下会指定数组的初始长度,然后进入ensureExplicitCapacity(int) 方法,然后再进入grow方法中进行数组扩容,如果数组为空则为将数组指定为默认长度,如果数组不为空且且添加元素后的新数组长度比原数组长度多则会使用Arrays.copyOf()方法进行1.5倍扩容。

HashMap

put()方法

(1)第一步:调用put方法传入键值对

(2)第二步:使用hash算法计算hash值

(3)第三步:根据hash值确定存放的位置,判断是否和其他键值对位置发生了冲突

(4)第四步:若没有发生冲突,直接存放在数组中即可

(5)第五步:若发生了冲突,还要判断此时的数据结构是什么?

(6)第六步:若此时的数据结构是红黑树,那就直接插入红黑树中

(7)第七步:若此时的数据结构是链表,判断插入之后是否大于等于8

(8)第八步:插入之后大于8了,就要先调整为红黑树,在插入

(9)第九步:插入之后不大于8,那么就直接插入到链表尾部即可。

自我介绍

面试官你好,我叫叶凯乐。我是来自哈尔滨理工大学计算机科学与技术专业的学生。本人在大学期间担任了国家级大学生创新创业项目“一物一码…”的负责人,在那其中。。。。,并最终结题,然后自己也做了另一个项目:电电商ERP系统…

为什么三次握手

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0xaEvE1L-1628170044503)(C:\Users\yekaile\AppData\Roaming\Typora\typora-user-images\image-20210310134210081.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoPaF28u-1628170044510)(C:\Users\yekaile\AppData\Roaming\Typora\typora-user-images\image-20210310134240617.png)]

操作系统管道

1、无名管道通信

无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2、高级管道通信

高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。

3、有名管道通信

有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

4、消息队列通信

消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5、信号量通信

信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6、信号

信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

7、共享内存通信

共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

8、套接字通信

套接字( socket ) : 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

之前写过一个课程设计:基于Internet的Linux客户机/服务器系统通讯设计与实现

是利用sock通信实现的,可以参考一下。

http解决粘包

http本身有content-length这个选项声明了传输文件body的大小是多少

解决哈希冲突

1, 开放定址法:
所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
公式为:fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1)
※ 用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者
碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表
中无待查的关键字,即查找失败。
比如说,我们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12。 我们用散列函数f(key) = key mod l2
当计算前S个数{12,67,56,16,25}时,都是没有冲突的散列地址,直接存入:


计算key = 37时,发现f(37) = 1,此时就与25所在的位置冲突。
于是我们应用上面的公式f(37) = (f(37)+1) mod 12 = 2。于是将37存入下标为2的位置:

2, 再哈希法:
再哈希法又叫双哈希法,有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数
计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间。

3, 链地址法:
链地址法的基本思想是:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向
链表连接起来,如:
键值对k2, v2与键值对k1, v1通过计算后的索引值都为2,这时及产生冲突,但是可以通道next指针将k2, k1所在的节点连接起来,这样就解决了哈希的冲突问题

主键索引唯一索引的区别

主键索引是在创建主键时一起创建的,是基于主键约束而建立的,是不可以为空,也不可以重复。

唯一索是引基于唯一约束而建立的,可以为空不可以重复,主键索引本身就具备了唯一索引的功能。

簇族索引的选择顺序是主键,唯一索引,隐藏列。默认是主键,如果不设置主键就是表里的一个唯一索引,如果唯一索引也没有设置,是mysql在每行后加的三列之一的一个自增列,用来做簇族。就是你说的聚簇索引。

binlog

binlog模式
  binlog有三种模式:ROW(行模式), Statement(语句模式), Mixed(混合模式)

ROW(行模式):记录那条数据修改了,注意:记录的是这条记录的全部数据,即使只更新了一个字段,binlog里也会记录所有字段的数据

                 优点:他不记录sql语句的上下文信息,日志内容会非常清楚的记录每条数据详细的变更细节,即使只更新了一个字段,binlog里也会记录所有字段的数据。

缺点:binlog日志会非常大,mysql主从同步时,会产生大量磁盘IO

Statement(语句模式): 每一条会修改数据的sql都会记录在binlog中。

优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。

缺点:由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执行时候相同 的结果。另外mysql 的复制,像一些特定函数功能,slave可与master上要保持一致会有很多相关问题。

Mixed(混合模式):在Mixed模式下,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。

cms垃圾回收器

初始标记(CMS initial mark):初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;
并发标记(CMS concurrent mark):该阶段就是进行 GC Roots Tracing 的过程;
重新标记(CMS remark):为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短;
并发清除(CMS concurrent sweep)

破坏双亲委派模型

上面提到过双亲委派模型并不是一个强制性的约束模型,而是java设计者推荐给开发者的类加载器实现方式,在java的世界中大部分的类加载器都遵循这个模型,但也有例外,到目前为止,双亲委派模型主要出现过三次较大规模的“被破坏”情况。

双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前–即JDK1.2发布之前。由于双亲委派模型是在JDK1.2之后才被引入的,而类加载器和抽象类java.lang.ClassLoader则是JDK1.0时候就已经存在,面对已经存在的用户自定义类加载器的实现代码,Java设计者引入双亲委派模型时不得不做出一些妥协。为了向前兼容,JDK1.2之后的java.lang.ClassLoader添加了一个新的proceted方法findClass(),在此之前,用户去继承java.lang.ClassLoader的唯一目的就是重写loadClass()方法,因为虚拟在进行类加载的时候会调用加载器的私有方法loadClassInternal(),而这个方法的唯一逻辑就是去调用自己的loadClass()。JDK1.2之后已不再提倡用户再去覆盖loadClass()方法,应当把自己的类加载逻辑写到findClass()方法中,在loadClass()方法的逻辑里,如果父类加载器加载失败,则会调用自己的findClass()方法来完成加载,这样就可以保证新写出来的类加载器是符合双亲委派模型的。
双亲委派模型的第二次“被破坏”是这个模型自身的缺陷所导致的,双亲委派模型很好地解决了各个类加载器的基础类统一问题(越基础的类由越上层的加载器进行加载),基础类之所以被称为“基础”,是因为它们总是作为被调用代码调用的API。但是,如果基础类又要调用用户的代码,那该怎么办呢?
这并非是不可能的事情,一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK1.3时放进rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用独立厂商实现部部署在应用程序的classpath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器不可能“认识”之些代码,该怎么办?
为了解决这个困境,Java设计团队只好引入了一个不太优雅的设计:线程上下文件类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。有了线程上下文类加载器,JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型,但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI,JDBC,JCE,JAXB和JBI等。
双亲委派模型的第三次“被破坏”是由于用户对程序的动态性的追求导致的,例如OSGi的出现。在OSGi环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为网状结构。

java内存泄漏

1、静态集合类,如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

2、各种连接,如数据库连接、网络连接和IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

进入老年代的条件

1、对象经历了15次 Minor GC 依旧存活 (默认值 -XX:MaxTenuringThreshold = 15)

2、Survivor区中 同龄对象大小超过Survivor区空间的50%,大于此年龄的对象会进入老年代 (动态对象年龄判定) (其实是这个年龄以及低于这个年龄的对象占据超过Survivor 50% 1+2+3大于50% 则大于等于3岁的对象会进入老年代)

3、Minor GC后对象大小大于Survivor大小,会进入老年代 (空间担保机制)

4、大对象直接进入老年代 (-XX:PretenureSizeThreshold=1M 只对Serial和ParNew两款收集器有效)

fullgc发生情况

fullgc的原因
Full GC触发条件:

(1)System.gc()方法的调用

该方法不一定执行,但是执行的时候是fullgc。

(2)老年代空间不足

老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:

java.lang.OutOfMemoryError: Java heap space

为避免以上两种状况引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

(3)方法区空间不足

在HotSpot虚拟机中又被习惯称为永生代或者永生区,Permanet Generation中存放的为一些class的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:

java.lang.OutOfMemoryError: PermGen space

为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

(4)CMS GC时出现promotion failed和concurrent mode failure

对于采用CMS进行老年代GC,要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。

promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的;concurrent mode failure是在

执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的(有时候“空间不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC)。

措施为:增大survivor space、老年代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。

对于这种状况,可通过设置-XX: CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

(5)Minor GC晋升到老年代的平均大小大于老年代的剩余空间

Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。

例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查老年代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。

当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。

(6)堆中分配很大的对象

所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。

为了解决这个问题,CMS垃圾收集器提供了一个可配置的参数,即-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程,内存整理的过程无法并发的,空间碎片问题没有了,但提顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。

补充2种fullgc情况

1.未指定老年代和新生代大小,堆伸缩时会产生fullgc。

2.直接内存

Cms退化为serial gc的情况
Minor GC后存活的对象晋升到老年代时由于悲观策略的原因,有两种情况会触发Full GC,:

1.之前每次晋升的对象的平均大小 > 老年代剩余空间;

2.Minor GC后存活的对象超过了老年代剩余空间。

这两种情况都是因为老年代会为新生代对象的晋升提供担保,而每次晋升的对象的大小是无法预测的,所以只能基于统计,1个是基于历史平均水平,一个是基于下一次可能要晋升的最大水平。这两种情况都是属于promotion failure。

CMS失败,发生concurrent mode failure会引起Full GC,这种情况下会使用Serial Old收集器,是单线程的,对GC的影响很大。concurrent mode failure产生的原因是老年代剩余的空间不够,导致了和gc线程并发执行的用户线程创建的大对象(由PretenureSizeThreshold控制新生代直接晋升老年代的对象size阀值)不能进入到老年代,只要stop the world来暂停用户线程,执行GC清理。可以通过设置CMSInitiatingOccupancyFraction预留合适的CMS执行时剩余的空间。

新生代直接晋升到老年代的大对象超过了老年代的剩余空间,引发Full GC。

注意于promotion failure的区别,promotion failure指的是Minor GC后发生的担保失败。

Perm区回收
Perm永久代空间不足会触发Full GC,可以让CMS清理永久代的空间。设置CMSClassUnloadingEnabled即可。

System.gc()引起的Full GC,可以设置DisableExplicitGC来禁止调用System.gc引发Full GC 。

使用CMS(ConcMarkSweep)策略时,必须有:-XX:+CMSPermGenSweepingEnabled 和-XX:+CMSClassUnloadingEnabled
来配合同时启用,才可以对PermGen进行GC(实际主要参数为:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled)。

调优方案

(1)观察gc情况,结合代码,调整线程数,可以减少fullgc次数,但是计算时间消耗很大。

(2)由于这种频繁的fullgc只是在早上汇总时候产生,并且正常每天一次,所以解决方案是更换gc,将由一台机器进行汇总,其他机器不影响。

jvm的gc相关参数:

-Xmx4096m -Xms4096m

-XX:MetaspaceSize=256m

-XX:MaxMetaspaceSize=512m

-XX:SurvivorRatio=8

-XX:ParallelCMSThreads=8

-XX:+CMSParallelRemarkEnabled 开启降低标记停顿

-XX:+ExplicitGCInvokesConcurrent

-XX:+CMSPermGenSweepingEnabled

-XX:+CMSClassUnloadingEnabled

-XX:+PrintGCCause

-XX:+PrintGCDetails

-XX:+PrintHeapAtGC

-XX:+PrintTenuringDistribution

-XX:+UseConcMarkSweepGC

-XX:+PrintGCTimeStamps

-XX:+PrintGCDateStamps

-XX:CMSFullGCsBeforeCompaction=0

-XX:+UseCMSCompactAtFullCollection

-XX:CMSInitiatingOccupancyFraction=80

线程的5种状态

线程可以有如下5种状态:New 、Runnable 、Running 、Blocked 、Dead

concurrenthashmap

插入操作

1、判断Node[]数组是否初始化,没有则进行初始化操作
2、通过hash定位数组的索引坐标,是否有Node节点,如果没有则使用CAS进行添加(链表的头节点),添加失败则进入下次循环。
3、检查到内部正在扩容,就帮助它一块扩容。
4、如果f!=null,则使用synchronized锁住f元素(链表/红黑树的头元素)。如果是Node(链表结构)则执行链表的添加操作;如果是TreeNode(树型结构)则执行树添加操作。
5、判断链表长度已经达到临界值8(默认值),当节点超过这个值就需要把链表转换为树结构。
6、如果添加成功就调用addCount()方法统计size,并且检查是否需要扩容

初始化

ConcurrentHashMap在初始化时,首先会判断,哈希表是否已经初始化了。如果没有,则尝试进行初始化。
首先会判断sizeCtl的值。sizeCtl是用于多线程之间同步的一个互斥变量。当sizeCtl < 0时,表示已经有线程正在初始化哈希表或哈希表正在扩容,此时,不能再进行操作。
此处sizeCtl其实是实现了自旋锁的功能。自旋锁,即,获取锁失败时,让出CPU,稍后再进行尝试,重复这个过程,直到获得到锁为止。让出CPU的动作,是通过java中的Thread.yield()来实现的。

自选锁方式和死循环方式来判断sizeCtl的值,有什么不同?
当然是效率的不同。死循环时,程序会频繁读取sizeCtl的值,在满足条件之前,会浪费很多CPU周期。而自选锁的效率更高,因为当它判断sizeCtl不满足条件时,会主动让出CPU,此时,当前线程会处于ready状态,等待下一次被处理器选中并执行的机会。在这段时间里,其他的线程得以利用CPU周期。所以,自旋锁的效率更高。

        Node<K,V>[] tab; int sc;while ((tab = table) == null || tab.length == 0) { // 第一次检查if ((sc = sizeCtl) < 0)Thread.yield(); // lost initialization race; just spinelse if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {try {if ((tab = table) == null || tab.length == 0) {// 第二次检查int n = (sc > 0) ? sc : DEFAULT_CAPACITY;@SuppressWarnings("unchecked")Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];table = tab = nt;sc = n - (n >>> 2);}} finally {sizeCtl = sc;}break;}}return tab;}

/** Implementation for put and putIfAbsent /
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//1. 计算key的hash值
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;

2021-08-05相关推荐

  1. (四十五:2021.08.05)《利用深度学习对ecg信号进行分割》

    <Deep Learning for ECG Segmentation><利用深度学习对ecg信号进行分割> 讲在前面 摘要 1. 介绍 2. 算法 2.1 预处理 2.2 神 ...

  2. 《安富莱嵌入式周报》第227期:2021.08.23--2021.08.29

    往期周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 ...

  3. Doris Weekly FAQ】2021.07.19~2021.08.01

    观众朋友们: 晚上好! 欢迎收看[ Doris 近日要闻]~本次为您带来的是 2021年07月19日 - 2021年08月01日 的双周总结. Doris 社区周报每期会包含 FAQ 环节.我们会在社 ...

  4. 2021.08.09【普及组】模拟赛C组比赛总结

    文章目录 2021.08.09[普及组]模拟赛C组比赛总结 写在前面: T1 :[普及模拟]生产武器 题目大意: 正解: T2 :[普及模拟]城市连接 题目大意: 正解: T3 :[普及模拟]抢救文件 ...

  5. 纯Go实现的Firebase的替代品 | Gopher Daily (2021.08.11) ʕ◔ϖ◔ʔ

    每日一谚:Global variables should have longer names. Go技术生态 如何才能成功将Python切换到Go - https://itnext.io/opinio ...

  6. 2021年05月软件设计师真题透析

    2021年05月软件设计师上午真题及答案解析 1. 在 CPU 中,用( )给出将要执行的下一条指令在内存中的地址. A.程序计数器 B.指令寄存器 C.主存地址寄存器 D.状态条件寄存器 答案:A, ...

  7. 2020.08.05狂人日记:C#计时器与串口通信

    2020.08.05狂人日记:C#计时器与串口通信 串口通信学习笔记 问题及解决 串口通信学习笔记 哔哩哔哩学习"C#上位机开发串口通信编程"笔记 串口发送数据 try //防止出 ...

  8. PL2303HX----Family Software Newsletter #37 - (Updated 08/05/2017)

    原文地址::http://www.ifamilysoftware.com/news37.html Family Software Newsletter #37 - (Updated 08/05/201 ...

  9. GNSS数据下载网站整理,包括gamit、bernese更新文件地址[2021.08更新]

    本人博客园同名原创文章,展示到CSDN供大家参考,转载请声明地址:https://www.cnblogs.com/ydh2017/p/6474654.html 从事GNSS研究的小伙伴大都离不开GNS ...

  10. 【每日一知】带你走近5nm芯片 (2021.02.05 )

    [每日一知]带你走近5nm芯片 (2021.02.05 ) [每日一知]带你走近5nm芯片 (2021.02.05 ) ==一.简介== ==二.优势== ==三.现状== ============= ...

最新文章

  1. poj3565(最大权完美匹配)
  2. 重磅:国拨概算5.34亿!“新一代人工智能”重大项目项目申报指南发布
  3. 关于使用scrapy框架编写爬虫以及Ajax动态加载问题、反爬问题解决方案
  4. python3 信号量和线程池 semaphore ThreadPollExector
  5. Linux系统简介 、 安装Linux系统 、 RHEL6基本操作
  6. Python的pycurl库升级升级失败的解决方法
  7. Linux 磁盘管理 二(Raid、LVM、Quota)
  8. caffe特征提取/C++数据格式转换
  9. [Hei-Ocelot-Gateway ].Net Core Api网关Ocelot的开箱即用版本
  10. Go 语言泛型,简明入门教程
  11. python列表切片规则_Python 列表切片
  12. shiro 实现自己定义权限规则校验
  13. python 图像识别_AI场景,3步懂图像识别产品
  14. Weka 3.8.6安装与Weka 3.8.6功能介绍
  15. 每日学习笔记(13)
  16. 微信小程序码接口返回的二进制内容处理返回给前端展示
  17. 几道经典的面试题53
  18. 解决ubuntu18.04无法连接wifi问题
  19. Word中遇到的各种问题及解决方案
  20. Electron 主进程、渲染进程及进程间的通信

热门文章

  1. c语言程序设计冒泡排序在哪,C语言程序设计冒泡排序教学案例杨进
  2. lamp环境下phpwind,wordpress,discuz论坛的搭建全过程
  3. 【旧文集】解忧杂货店读后感悟-记于2016年底
  4. 三段式过流保护、差动保护
  5. XC6206P332MR的特点与典型应用电路
  6. 十六进制颜色值域RGB格式颜色值之间的相互转换
  7. ei拼音的四个声调对应的字_【ei的四个声调有汉字】作文写作问答 - 归教作文网...
  8. java之一篇:java的执行顺序表象
  9. 男孩子取名起名字:无私慷慨 拼搏自强的男孩名字
  10. 推荐开发工具系列之--PyF5(自动刷新)