Java基础

java的三大特性:

  1. 封装:隐藏对象的属性和实现细节,对外仅提供公共访问方式,提高复用性和安全性。在java当中有3中修饰符 public、private、protected赋予不同的访问权限。

  2. 继承:提高了从基类获取字段和方法的能力,提高代码复用性,继承是多态的前提。

  3. 多态:同一个行为具有多个不同表现形式或形态的能力。

    ps:抽象:把想法从具体的实例中分离出来的步骤,根据功能而不是实现细节来创建类。java支持创建只暴漏接口而不包含方法的抽象的类。

四个访问修饰符级别

  • public : 对所有类可见。使用对象:类、接口、变量、方法

  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

  • default (即默认,什么也不写): 在同一包内可见。使用对象:类、接口、变量、方法。

  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

  • 修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
    public Y Y Y Y Y
    protected Y Y Y Y/N(是否与基类同包) N
    default Y Y Y N N
    private Y N N N N

final

修饰符

  1. 一个类被声明为final,意味着不能被继承。因此一个类不能同时声明为abstract又被声明为final的。
  2. 变量或方法声明为final表示是不能改变的,在声明final修饰的变量时必须设定初值,在之后的引用中只能进行读取,不能修改。

int 和Integer的区别

  1. int是基本类型,直接存值。
  2. integer是对象,通过引用指向这个对象,比int占内存大(对象需要描述数据结构)。
  3. Integer是int的包装类,java se5之后在装箱与拆箱中两者自动转换。(装箱=》int变成Integer)

方法重写和重载(多态)

  1. **override(重写):**参数类型、方法名、返回值类型一致,不能抛出更广泛的异常,访问权限不能比父类低。构造方法、static和final修饰的方法不能被重写
  2. overlord(重载):相同方法名,可以改变参数列表、返回值类型、访问修饰符,可以抛出更广泛的异常。无法以返回值类型作为重载函数的区分标准

抽象类和接口的区别

  1. 接口是公开的,不能包含私有的方法或变量,抽象类可以有私有的方法或者私有变量;
  2. 实现接口必须实现接口内定义的所有方法,抽象类可以有选择地重写需要用到的方法。在一般的应用中,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现
  3. 接口可以实现多重继承,而一个类只能继承一个超类

反射

java反射的主要功能:

  1. 在运行时确定一个对象所属的类
  2. 取出类的修饰符、数据成员、方法、构造器、超类
  3. 运行时判断一个类所具有的成员变量和方法
  4. 在运行时构造一个类的对象
  5. 在运行时调用动态对象的方法
  6. 创建数组,运行时才能确定数组的大小和类型,也能更改数组成员的值

反射的应用

  1. spring的 ioc/di
  2. JDBC的ForName()
  3. javaBean和jsp之间调用

框架大量使用反射将导致性能大打折扣

Class类对象

  1. Class Class.forName(String packageNameAndClassName);
    根据指定的完整包名.类名获取对应的Class对象。存在异常抛出
    ClassNotFoundException
  2. Class 类对象.getClass();
    通过类对象获取当前类对象对应的Class对象。
  3. Class 类名.class;
    通过类名获取class属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggWC4hOn-1650358644425)(img\反射内存分析.png)]

get和post请求的区别

  1. GET和POST本质都是TCP链接。
  2. GET请求在URL中传送,参数有长度限制的,而POST没有。
  3. POST比GET更安全,因为get请求参数直接暴露在URL上。
  4. GET速度比POST速度快,GET请求浏览器会把http header和data同时传递,只有一个TCP数据包,POST两个TCP数据包,先发送header,服务器响应 100 ,浏览器再发送data,服务器响应 200
  5. GET在浏览器回退时是无害的,而POST会再次提交请求。
  6. GET请求会被浏览器主动cache,而POST不会,除非手动设置。

session 和 cookie区别

  1. session在调用getsession()时创建,通过cookie内的sessionid获取,保存在服务器中。
  2. 安全性高于cookie,过大可能造成服务器负担,只存放用户极重要信息
  3. 当服务器关闭、超过设定生存时间、调用 invalidate()等方法时 session失效。
  4. cookie分为会话cookie持久化cookie,会话cookie存放sessionID,保存在浏览器中,当浏览器关闭时,会话cookie消失。
  5. 持久化cookie保存在硬盘,超过保存时间后消失。

equals与==的区别

值类型(boolean、int、long、char等)都是通过==判断相等性。对象引用的话,判断引用所指的对象是否是同一个。

equals是Object的成员函数,判断引用指向空间的值是否相同。

标识符命名规则

  1. 有且只能使用英文字母(A ~ Z, a ~ z), 数字(0 ~ 9)以及唯一可以使用的标点符号 _ (下划线),开头使用字母。
  2. 严格区分大小写,长度不限制(一般不超过15个),不能是Java中的关键字和保留字。

基本数据类型转换

自动类型转换:数据类型取值范围大的转成小的;

强制类型装换:数据类型取值范围小的转成大的.

Ajax跨域问题

  1. Ajax发送请求时,不允许跨域,以防用户信息泄露。
  2. 当Ajax跨域请求时,响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。
  3. 互相信任的域之间如果需要ajax访问,(比如前后端分离项目中,前端项目和后端项目之间),则需要使用@CrossOrigin标记到对应的类或者方法上。

四种引用

  1. 强引用:正常写的引用都是强引用,JVM会根据是否可达判断是否要回收
  2. 软引用:强引用里面的引用,指被关联着的对象
  3. 弱引用:System.out.println(msg.get());
  4. 虚引用:管理堆外内存,用来标记哪些堆外内存需要回收

jdk8 新特性

  1. Lambda表达式:允许把函数作为一个方法的参数
  2. 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  3. 默认方法:默认方法就是一个在接口里面有了一个实现的方法。
  4. Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  5. DateTime API:加强对日期与时间的处理。
  6. Optional类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

Java序列化

java提供的一种对象序列化的机制,对象可以被表示为一个字节序列,将序列化对象写入文件后可以从文件中读取出来,并进行反序列化。

集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3Bb4B3t-1650358644425)(\img\集合.jpg)]

ArrayList数据结构

  1. ArrayList 是基于定长数组实现的变长的集合类,使用默认构造方法初始化出来的容量是 10(1.7 之后延迟初始化,即第一次调用 add 方法时才将 elementData 容量初始化为10)
  2. ArrayList 允许空值和重复元素,当添加的元素数量大于其底层数组容量时,通过扩容机制生成原数组1.5倍长度的新数组。自动将原来数组的元素向新的数组进行 copy。
  3. 顺序添加很方便,删除和插入需要数组整体移动,性能差。
  4. ArrayList 是非线程安全类,并发环境多个线程同时操作会引发不可预知的异常或错误。

LinkList的数据结构

  1. LinkedList是基于链表实现的一种线性存储结构。
  2. 将要存储的数据存在一个存储单元内,除了存放有待存储的数据以外,还存储其下一个存储单元的地址,有些存储结构还存放有其前一个存储单元的地址。
  3. 每次查找数据的时候,通过某个存储单元中的下一个存储单元的地址寻找其后面的那个存储单元。
  4. 插入删除数据方便仅需要修改上下节点的指向,查找数据需要一个一个查找,查询效率低。

CopyOnWriteArrayList数据结构

  1. 并发优化的ArrayList。用CopyOnWrite策略,在修改时先复制一个快照来修改,改完再让内部指针指向新数组。
  2. 因为对快照的修改对读操作来说不可见,所以只有写锁没有读锁,适合读多写少的场景。

HashMap数据结构

Hashmap 是基于哈希表实现 map 接口的非同步实现,key 、value 可以且只能有一条为null。根据键的 HashCode 值存储数据,具有很快的访问速度,不支持线程同步。HashMap 是无序的,即不会记录插入的顺序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OFFX0bTV-1650358644426)(img\hashMap.jpg)]

  1. 以Entry[]数组实现的哈希桶数组,用Key的哈希值取模桶数组的大小可得到数组下标。
  2. 插入元素时,如果两条Key落在同一个桶(比如哈希值1和17取模16后都属于第一个哈希桶),Entry用一个next属性实现多个Entry以单向链表存放,后入桶的Entry将next指向桶当前的Entry。
  3. 查找哈希值为17的key时,先定位到第一个哈希桶,然后以链表遍历桶里所有元素,逐个比较其key值。
  4. 当Entry数量达到桶数量的75%时,桶数组扩容一倍,并重新分配所有原来的Entry。
  5. 取模用位运算(hash & (arrayLength-1)),数组的大小是2的N次方,数据在数组上分布较为均匀,默认第一次放入元素时的初始值是16。
  6. iterator()时顺着哈希桶数组来遍历,乱序存储。
  7. 在JDK8里,新增默认为8的閥值,当一个桶里的Entry超过閥值,就不以单向链表而以红黑树来存放以加快Key的查找速度。
  8. 链表长度超过8,但是HashMap的总体容量不超过64,此时优先扩容减少hash冲突,也避免形成树后再次扩容对树的拆分,小于6退化。

TreeMap数据结构

  1. 以红黑树实现。支持iterator()时按Key值排序,可按实现了Comparable接口的Key的升序排序,或由传入的Comparator控制。在树上插入/删除元素的代价比HashMap的大。
  2. 支持SortedMap接口,如firstKey(),lastKey()取得最大最小的key,或sub(fromKey, toKey), tailMap(fromKey)剪取Map的某一段。

红黑树

https://blog.csdn.net/zhangting19921121/article/details/104673519

  1. 红黑树存储:其访问性能相较于链表接近于折半
  2. 红黑树的存储方式类似于平衡的二叉查找树,其优点是左右子树高度几乎一致,防止树退化为链表。
  3. 红黑树的特点:
    1. 每个节点或者是黑色,或者是红色。
    2. 根节点是黑色。
    3. 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!
    4. 如果一个节点是红色的,则它的子节点必须是黑色的。
    5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

ConcurrentHashMap数据结构

  1. 使用锁分段技术,并发优化的HashMap,默认16把写锁(可以设置更多),有效分散了阻塞的概率,没有读锁。
  2. 没有读锁是因为put/remove动作是个原子动作(比如put是一个对数组元素/Entry 指针的赋值操作),读操作不会看到一个更新动作的中间状态。
  3. 数据结构为Segment[],内部才是是哈希桶数组,每个Segment一把锁。Key先算出它在哪个Segment里,再算出在哪个哈希桶。
  4. 当部分方法需要跨段访问比如:size()、containsValue()时,按顺序对所有段进行加锁。操作完毕后按顺序释放所有段的锁,如果不按顺序可能发生死锁。
  5. 在内部将数组声明为 final 的,但数组成员不是 final 的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。

LinkedHashMap数据结构

  1. 扩展HashMap增加双向链表的实现,号称最占内存的数据结构。支持iterator()时按Entry的插入顺序来排序
  2. 实现上是在Entry上再增加属性before/after指针,插入时把自己加到Header Entry的前面去。
  3. 如果读写访问都要排序,要把前后Entry的before/after拼接起来。

hashcode 和 equals 的区别

  1. Java 中的对象在 JVM 在管理下会因为整理内存碎片的GC算法下进行移动,对象的地址会变动,但 hashcode 不会改变。
  2. hashCode 是为了提高在散列结构存储中查找的效率,在线性表中没有作用。
  3. 一般一个类的对象如果会存储在 HashTable,HashSet,HashMap 等散列存储结构中,那么重写 equals 后也重写 hashCode,否则会导致存储数据的不唯一性(存储了两个equals 相等的数据)。
  4. 如果确定不会存储在这些散列结构中,则可以不重写 hashCode。
  5. 同一对象在执行期间若已经存储在集合中,则不能修改影响 hashCode 值的相关信息,否则会导致内存泄露问题。
  6. 一般来说涉及到对象之间的比较大小就需要重写 equals 方法。

List和set的区别

  1. List和set都继承自Collection接口

  2. List元素有放入顺序,可重复。和数组类似,能够动态增长,查找元素效率高,插入删除元素效率低,因为可能引起其他元素位置改变。

  3. Set元素无放入顺序,元素放入位置由该元素的HashCode决定,元素不可重复,重复元素会进行覆盖。检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。Set几乎都是内部用一个Map来实现,,Map里的KeySet就是一个Set,value是假值全部使用同一个Object。

List和Map的区别

  1. List是对象集合,允许对象重复
  2. Map是键值对集合,不允许key重复

ArrayList、LinkList、Vector区别

  1. ArrayLis和Vector使用数组方式存储数据,索引数据快插入数据慢。
  2. Vector使用了synchronized方法,性能上较ArrayList差,但是线程安全。
  3. Vector可以设置增长因子,ArrayList不可以。
  4. LinkedList 使用双向链表实现存储,索引速度慢插入速度较快。

HashMap和HashTable的区别

  1. hashMap去掉了HashTable的contains方法,增加了containsValue()和containsKey()方法。
  2. HashTable同步,HashMap非同步的
  3. HashMap允许空键值,HashTable不允许

HashSet和HashMap区别

  1. set是线性结构,set中值不能重复。HashSet是set的hash实现,用HashMap的key来实现的。
  2. map是键值对映射,可以空键空值。HashMap是map的hash实现,key的唯一性是通过key值的hash值的唯一来确定的,value值则是链表结构。
  3. 两者都是hash算法实现的,都不能持有基本类型,只能持有对象。

线程

线程和进程的区别

进程:拥有资源的基本单位。有独立的地址空间,即使崩溃在保护机制下不会影响其他进程,进程创建、撤销(分配和回收资源),切换消耗资源较大。

线程:进程中一部分,调度和分配的基本单位。有自己的堆栈和局部变量,没有单独的地址空间,一个线程死掉等于整个进程死掉。作用:可以使用多线程提高密集型任务运算速度

创建线程的方式

java创建线程主要有三种方式:

  1. 继承Thread类,定义Thread类的子类并重写run()方法,run方法为执行体。创建Thread子类的实例即创建了线程对象,调用线程对象star()方法来启动该线程。
  2. 定义实现Runable接口的实现类,重写该接口的run()方法,run方法为执行体。创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象,调用线程对象的start()方法启动该线程。
  3. 通过Callable和Future创建线程,创建Callable接口的实现类并实现call()方法,call()方法作为执行体,并且有返回值。创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。使用FutureTask对象作为Thread对象的target创建并启动新线程,调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

创建线程三种方式对比:

  1. 采用Runnable、Callable接口创建线程

    线程只是实现了Runnable或Callable接口,还可以继承其他类。多个线程可以共享一个target对象,适合多个相同线程来处理同一份资源的情况,可以将CPU、代码和数据分开。但是编程稍微复杂,访问当前线程必须使用Thread.currentThread()方式。

  2. 使用继承Thread类的方式创建多线程

    编写简单,如果需要访问当前线程,直接使用this即可获得当前线程。但是已经继承了Thread类,不能继承其他父类。

线程的生命周期

  1. 新建:创建Thread类的一个实例时,此线程进入新建状态(未被启动)

  2. 就绪:线程已被启动,正在等待被分配CPU时间片

  3. 运行:线程获取CPU资源正在执行任务(run()方法),此时除非线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束

  4. 死亡:当线程执行完毕或者被其他线程杀死,线程就会进入死亡状态,此时线程不可能再进入就绪状态等待执行。

    自然终止:正常运行run()方法后终止;

    异常终止:调用stop()方法后让一个线程终止

  5. 堵塞:由于某种原因导致正在运行的线程让出CPU并暂停自己的执行。

    正在睡眠:用sleep(long t)方法睡眠指定时间后进入就绪状态

    正在等待:调用wait()方法(调用notify()方法回到就绪状态)

    被另外一个线程所阻塞:调用suspend()方法(调用resume()方法恢复)

sleep()、join()、yield()有什么区别

  1. sleep让当前正在执行的线程休眠指定的毫秒数,此操作受到系统计时器和调度程序精准和精准性的影响,并不释放对象锁,可以使低优先级的线程得到执行的机会,也可以让同优先级、高优先级的线程有执行的机会。
  2. Thread的join()方法让以一个线程B加入到另一个线程A的尾部,在A执行完毕之前,B不能工作。
  3. yield()方法和sleep()方法类似,不会释放”锁标志“,区别在于他没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会。

Sleep和Wait

  1. sleep()属于Thread类,使程序暂停执行指定的时间,但监控状态依然保持,会自动恢复运行状态,线程不会释放锁。
  2. wait()方法线程会放弃对象锁,进入等待此对象的等待锁定池,调用notify()方法后,该线程才会进入对象锁定池准备,获取锁对象进入运行状态。
  3. 最主要的sleep方法没有释放锁,而wait方法释放了锁,使其他线程可以使用同步控制或者方法。wait,notify和notifyAll只能在同步控制方法或者同步控制快里面使用,而sleep可以在任何地方使用。

CountDownLatch(闭锁)

  1. CountDownLatch是同步工具类之一,可以指定一个计数值,在并发环境下由线程进行减1操作,当计数值变为0之后,被await方法阻塞的线程将会唤醒,实现线程间的同步。例如:有一个任务A,他要等待其他几个任务执行完毕后才能执行。
  2. CountDownLatch类只提供了一个构造器,传入count计数值。
  3. 需要挂起的线程需要调用await()方法,该线程便会等待count值为0才继续执行。
  4. countDown()方法用于count值减1。

CyclicBarrier(回环栅栏)

  1. CyclicBarrier可以让一组线程等待至某个状态后再全部同时执行。当所有等待线程都被释放后,CyclicBarrier可以被重用。当调用await()方法后,线程就处于barrier了。

  2. CyclicBarrier拥有两个构造函数。

    public CyclicBarrier(int parties, Runnable barrierAction) {}
    public CyclicBarrier(int parties) {}
    
  3. 参数parties指让几个线程或者任务等待barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

  4. CyclicBarrier的await()方法有两个版本,一个是挂起当前线程,另一个是让这些线程等待至一定时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。

CountDownLatch与CyclicBarrier区别

  1. CountDownLatch减计数方式,计算为0时释放所有等待的线程,计数为0时无法重置。调用countDown()方法计数减一,调用await()方法仅进行阻塞,对计数没有影响。
  2. CyclicBarrier加计数方式,计数达到指定值时释放所有等待线程。计数达到指定值时,计数置为0重新开始。调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。

Semaphore(信号量)

Semaphore可以控制同时访问的线程个数,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。Semaphore和锁有点类似,一般用于对某组资源的访问控制。

Exchanger(交换器)

  1. 此类提供对外的操作是同步的
  2. 用于成对出现的线程之间交换数据
  3. 可以视作双向的同步队列
  4. 当一个线程到达exchange调用点是,如果他的伙伴线程此前已经调用了此方法,那么他的伙伴会被调度唤醒并与之进行对象交换,然后各自返回。如果他的伙伴线程还没到达交换点,那么当前线程会被挂起,直至伙伴线程到达,完成交换正常返回。或者当前线程被终端或者等候超时,则抛出相应异常。

Threadlocal

  1. 我们使用ThreadLocal为每个使用该类型的变量提供了一个独立的副本
  2. 具体的实现是在每个线程中保存了一个ThreadLocalMap
  3. 这个ThreadLoaclMap会在我们第一次使用ThreadLoal中的set方法创建出来
  4. set方法就是保存在ThreadLocalMap中,该变量为key,值为value
  5. get方法也从这个HashMap中找
  6. ThreadLocal 是一个本地线程副本变量工具类
  7. 主要用于将私有线程和该线程存放的副本对象做一个映射
  8. 各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用
  9. 特别适用于各个线程依赖不通的变量值完成操作的场景。
  10. 简单说 ThreadLocal 就是一种以空间换时间的做法,在每个 Thread 里面维护了一个以开地址法实现的 ThreadLocal.ThreadLocalMap,
    把数据进行隔离
  11. 数据不共享,自然就没有线程安全方面的问题

线程池

  1. 系统启动时即创建大量空闲的线程。
  2. 程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务。
  3. 当任务执行完毕后线程并不会死亡,而是返回线程池中成为空闲状态。
  4. 系统也可以同时向线程池中提交多个任务。
  5. 减少对象创建销毁的开销,提高系统资源的使用率。
  6. 具有定时执行、定期执行、单线程、并发数控等功能。

线程池5种状态


Running(运行):线程池初始化状态,一旦被创建,就已经处于Running状态,线程池种任务为0。处于Runnimg状态时能够接收新任务,以及对已经添加的任务进行处理。

ShutDown(关闭):调用线程池的ShutDown接口时,线程池进入ShutDown状态,不接收新任务,但能处理已添加的任务。

Stop(停止):调用ShutDownNow接口时,线程池进入Stop状态,不接收新任务,也不处理已经添加的任务,并且中断正在处理的任务。

Tidying(整理):当所有任务已终止,ctl记录的"任务数量"为0,线程池进入Tidying状态,执行钩子函数terminated()。如果希望线程池进入整理状态时进行相应的处理,可以通过重载钩子函数来实现。

Terminated(终结):线程池处于整理状态时,执行完钩子函数进入终结状态,线程池彻底终止。

线程池分类

  1. newFixedThreadPool 固定大小线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量。当线程发生未预期的错误而结束时,线程池会补充一个新的线程(经典线程池)
  2. newCachedThreadPool 可缓存线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制,线程数 JVM 控制。
  3. newSingleThreadExecutor 单线程线程池,创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代,能确保依照任务在队列的顺序来串行执行 (保证顺序)
  4. newScheduleThreadPool 定时线程池,固定长度,而且以延迟或定时的方式来执行任务(可延时或定期执行)
  5. newSingleThreadScheduledExecutor 单线程可定时执行线程池

线程池参数

ThreadPoolExecutor (

int corePoolSize–核心线程数,

int maximumPoolSize–最大线程数,

long keepAliveTime–线程存活时间-非核心线程,

TimeUnit unit–存活时间单位,

BlockingQueue workQueue–任务队列,

ThreadFactory threadFactory–创建线程的工程,

RejectedExecutionHandler handler–拒绝策略 )

线程池拒绝策略

  1. AbortPolicy();直接抛出RejectedExecutionException,并丢弃任务。
  2. DiscardPolicy();不抛异常,直接丢弃任务。
  3. DiscardOldestPolicy();将最早进入队列的任务丢弃,之后再尝试加入队列。
  4. CallerRunsPolicy();添加线程池失败,由主线程自己去执行。

线程之间实现通信

  1. volatile:内存共享,具有可见性、有序性。无法保证原子性操作
  2. 等待 / 通知机制:基于wait和notify方法实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待,直到被通知或者被唤醒。
  3. CountDownLatch:一个线程或多个,等待另外N个线程完成某个事情之后才能执行。
  4. CyclicBarrier:N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

保证线程安全的方式

同步代码块、同步方法、使用ReentrantLock

volatile的内存语义

  1. volatile的作用是保证可见性以及有序性 。
  2. 类型修饰符。
  3. volatile修饰的变量当一个线程修改变自己本地工作内存的变量值后,强制将这个变量最新的值刷回主内存,并强制让其他工作内存中变量缓存直接失效过期。

synchronized 作用,如何实现线程间通信

  1. synchronized 通过修改对象头的锁状态(对象头:hashcode、分代年龄、gc标记、锁状态),让每个线程进入方法时都进行加锁。
  2. 同一时间只能有一个线程加锁,其他线程需要等待锁,以此来保证线程安全。
  3. 在线程之间通过在synchronized内代码对不同线程的互斥性进行通信。

synchronized与Locker的区别

  1. 主要相同点:Lock 能完成 synchronized 所实现的所有功能
  2. 主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。
  3. synchronized是java内置关键字,在jvm层面,Lock是个java类。
  4. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁。
  5. synchronized 自动释放锁,而 Locker 手工释放,并且必须在 finally 从句中释放。
  6. 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了。
  7. .synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
  8. Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

乐观锁与悲观锁

  1. 乐观锁:认为竞争不总是会发生,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
  2. 悲观锁:认为竞争总是会发生,每次对某资源进行操作时,都会持有一个独占的锁,就像 synchronized,直接上了锁操作资源。

什么是CAS

  1. cas(Compare and set)先比较再设置,用于解决并发执行修改一个数的值使用synchronized锁这种重量级机制的问题。
  2. 一个线程拿到当前值,进行比较如果数值没有改变就说明没有别的线程修改过,直接对该值进行修改。如果进行比较时发现值发生了改变,说明其他线程已经对该值进行了修改,则进入无限循环,重新获取值进行比较。
  3. 当大量的线程同时并发修改一个值,可能有很多线程会不停自旋,进入一个无限循环。

乐观锁的业务场景及实现方式

每次获取数据的时候,都不担心数据被修改,但是在更新数据时需要判断该数据是否被别人修改过。如果被其他线程修改,则不进行数据更新,如果没有被其他线程修改则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。

比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

MySQL

索引

  1. 排好序的快速查找数据结构
  2. 索引本身也很大,不可能全部存储在内存中,因此索引常常以索引文件的形式存储在磁盘上。
  3. 如果没有特别指明,都指B树(多路搜索树,并不一定是二叉的)
  4. 提高数据检索效率,降低IO成本。降低排序成本,降低CPU消耗
  5. 索引需要占用空间
  6. 降低更新表的速度
  7. 频繁查找条件的字段应创建索引
  8. where条件用不到的字段不创建索引

索引的类型

  1. UNIQUE(唯一索引):不可以出现相同的值,可以有NULL值
  2. INDEX(普通索引):允许出现相同的索引内容
  3. PROMARY KEY(主键索引):不允许出现相同的值
  4. fulltext index(全文索引):可以针对值中的某个单词,但效率较低
  5. 组合索引:将多个字段建到一个索引里,列值的组合必须唯一

创建索引

  1. 使用alter table语句创建索引

    应用于表创建完毕之后再添加

    ALTER TABLE table_name(表名) ADD 索引类型 索引名 (colun_list(字段名));
    

    ALTER TABLE可以用于创建普通索引、UNIQUE索引、PROMARY KEY索引三种索引格式,table_name是要增加索引的表名,column_list值对那些列进行索引,多列时各列之间用逗号分隔,索引名可选。

  2. 使用create index语句新增索引

    可用于建表时增加普通索引或unique索引

    CREATE INDEX index_name ON table_name(column_list=>name(length));
    

    如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。table_name、index_name和column_list具有与ALTER TABLE语句中相同的含义,索引名不可选。另外,不能用CREATE INDEX语句创建PRIMARY KEY索引

删除索引

​ 删除索引可以使用ALTER TABLE或DROP INDEX语句来实现。DROP INDEX可以在ALTER TABLE内部作为一条语句处理

drop index index_name on table_name ;alter table table_name drop index index_name ;alter table table_name drop primary key ;

前两条语句中,都删除了table_name中的索引index_name。而在最后一条语句中,只在删除PRIMARY KEY索引中使用,因为一个表只可能有一个PRIMARY KEY索引,因此不需要指定索引名。如果没有创建PRIMARY KEY索引,但表具有一个或多个UNIQUE索引,则MySQL将删除第一个UNIQUE索引。

如果从表中删除某列,则索引会受影响。对于多列组合的索引,如果删除其中的某列,则该列也会从索引中删除。如果删除组成索引的所有列,则整个索引将被删除。

索引的使用

EXPLAIN可以帮助开发人员分析SQL问题,explain显示了mysql如何使用索引来处理select语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句。使用时在select语句前加上Explain就可以了

Explain select * from user where id=1;

索引使用技巧

1.索引不会包含有NULL的列

​ 只要列中包含有NULL值,都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。

2.使用短索引

​ 对串列进行索引,如果可以就应该指定一个前缀长度。例如,如果有一个char(255)的列,如果在前10个或20个字符内,多数值是唯一的。那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

3.索引列排序

​ mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作,尽量不要包含多个列的排序,如果需要最好给这些列建复合索引。

4.like语句操作

like ‘%aaa%’不会使用索引,而like ‘aaa%’可以使用索引。

5.不要在列上进行运算

6.不使用NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的。使用不等号mysql无法使用索引。

7.索引要建立在经常进行select操作的字段上。

​ 如果这些列很少用到,那么有无索引并不能明显改变查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

8.索引要建立在值比较唯一的字段上

9.对于那些定义为text、image和bit数据类型的列不应该增加索引。因为这些列的数据量要么相当大,要么取值很少。

10.在where和join中出现的列需要建立索引

11.如果where字句的查询条件里使用了函数(如:where DAY(column)=…),mysql将无法使用索引。

12.在join操作中(需要从多个数据表提取数据时),mysql只有在主键和外键的数据类型相同时才能使用索引,否则不会使用索引。

索引的缺点

  1. 索引大大提高了查询速度,但会降低更新表的速度。如对表进行INSERT,UPDATE和DELETE,因为更新表时mysql不仅要保存数据,还要保存索引文件。
  2. 建立索引会占用磁盘空间的索引文件,在大表上建立多种组合索引,索引文件过大。

SQL优化

  1. 对查询进行优化,应尽量避免全表扫描,首先考虑where及order by涉及的列上建立索引。
  2. 尽量避免在where子句中对字段进行null值判断,否则将放弃索引。
  3. 尽量避免在where子句中使用!=或<>操作符,否则将放弃索引。
  4. 尽量避免在where子句中使用or来连接条件,否则将放弃索引。
  5. 慎用in和not in,连续的数值使用between,否则容易全表扫描
  6. list 匹配字符前面使用”%“将全表扫描
  7. 尽量避免在where子句中对字段进行表达式或函数操作,否则放弃索引。
  8. 索引字段作为条件时,如果该索引时复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,并应尽可能的将字段顺序与索引顺序相一致。
  9. 大量创建索引能提高select的效率,但同时会降低insert和update的效率,一张表的索引最好不要超过6个。
  10. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
  11. 尽可能使用varchar代替char,节省存储空间
  12. 不要返回用不到的任何字段
  13. 避免频繁创建和删除临时表,减少系统资源的消耗
  14. 尽量避免大事务操作,提高系统并发能力。
  15. 尽量避免向客户端返回大数据量,若数据量过大,应考虑相应需求是否合理。

MySQL的锁

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低

行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高

页面锁:开销和加锁时间位于以上两种之间;会出现死锁;锁定颗粒度介于表锁和行锁之间,并发度一般

什么是死锁

所谓死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,他们都将无法推进下去。此时系统处于死锁状态,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁,所以解决死锁主要还是针对于最常用的InnoDB。

死锁的关键在于:两个(或以上)的session加锁的顺序不一致。

产生死锁的四个必要条件

  1. 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
  2. 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
  3. 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
  4. 环路等待条件:是指进程发生死锁后,必然存在一个进程–资源之间的环形链

B-tree

B-tree具有良好的定位特性,常被用于对检索时间要求苛刻的场合

  1. B-tree索引时数据库中存取和查找文件(称为记录或键值)的一种方法
  2. 硬盘中的结点也是B-tree结构的,与内存相比,硬盘必须花费成倍的时间来存取一个数据元素,与一个节点两个分支的二元树相比,B-tree利用多个分支的结点,减少获取记录时所经历的结点数,从而达到节省存取时间的目的

聚集索引和非聚集索引

聚集索引:按照数据的物理存储进行划分的,对于一堆记录来说,使用聚集索引就是对这堆记录进行堆划分。

非聚集索引:强调的是逻辑分类,定义了一套存储规则,需要有一个控件维护这个规则,称为索引表。

存储引擎

  1. myISAM:管理非事务表,提供高速存储和检索,以及全文搜索能力。不支持事务,默认的存储引擎。
  2. memory:默认使用hash索引,数据存储在内存中
  3. innoDB:提供了事务、回滚以及系统崩溃修复能力

MongoDB和MySQL和Redis的区别

MySQL:

1、在不同的引擎上有不同的存储方式。
2、查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高。
3、开源数据库的份额在不断增加,mysql的份额页在持续增长。
4、缺点就是在海量数据处理的时候效率会显著变慢。

MongoDB:

Mongodb是非关系型数据库(nosql ),属于文档型数据库。文档是mongoDB中数据的基本单元,类似关系数据库的行,多个键值对有序地放置在一起便是文档,语法有点类似javascript面向对象的查询语言,它是一个面向集合的,模式自由的文档型数据库。
存储方式:虚拟内存+持久化。
查询语句:是独特的Mongodb的查询方式。
适合场景:事件的记录,内容管理或者博客平台等等。
架构特点:可以通过副本集,以及分片来实现高可用。
数据处理:数据是存储在硬盘上的,只不过需要经常读取的数据会被加载到内存中,将数据存储在物理内存中,从而达到高速读写。
成熟度与广泛度:新兴数据库,成熟度较低,Nosql数据库中最为接近关系型数据库,比较完善的DB之一,适用人群不断在增长。
优点:
快速!在适量级的内存的Mongodb的性能是非常迅速的,它将热数据存储在物理内存中,使得热数据的读写变得十分快。高扩展性,存储的数据格式是json格式!
缺点:
不支持事务操作。MongoDB本身没有自带事务机制,若需要在MongoDB中实现事务机制,需通过一个额外的表,从逻辑上自行实现事务。
应用经验少,由于NoSQL兴起时间短,应用经验相比关系型数据库较少。
MongoDB占用空间过大。

Redis 与MongoDB对比:

1、内存管理机制
Redis 数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。
MongoDB 数据存在内存,由 linux系统 mmap 实现,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。
2、支持的数据结构
Redis 支持的数据结构丰富,包括hash、set、list等。
MongoDB 数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。
3、数据量和性能:
当物理内存够用的时候,redis>mongodb>mysql
当物理内存不够用的时候,redis和mongodb都会使用虚拟内存。
实际上如果redis要开始虚拟内存,那很明显要么加内存条,要么你换个数据库了。
但是,mongodb不一样,只要,业务上能保证,冷热数据的读写比,使得热数据在物理内存中,mmap的交换较少。
mongodb还是能够保证性能。
4、性能
mongodb依赖内存,TPS较高;Redis依赖内存,TPS非常高。性能上Redis优于MongoDB。
5、可靠性
mongodb从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性;
Redis依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能。
可靠性上MongoDB优于Redis。
6、数据分析
mongodb内置数据分析功能(mapreduce);而Redis不支持。
7、事务支持情况
Redis 事务支持比较弱,只能保证事务中的每个操作连续执行;mongodb不支持事务。
8、集群
MongoDB 集群技术比较成熟,Redis从3.0开始支持集群。

Mysql和Mongodb主要应用场景:

1.如果需要将mongodb作为后端db来代替mysql使用,即这里mysql与mongodb 属于平行级别,那么,这样的使用可能有以下几种情况的考量: (1)mongodb所负责部分以文档形式存储,能够有较好的代码亲和性,json格式的直接写入方便。(如日志之类) (2)从datamodels设计阶段就将原子性考虑于其中,无需事务之类的辅助。开发用如nodejs之类的语言来进行开发,对开发比较方便。 (3)mongodb本身的failover机制,无需使用如MHA之类的方式实现。
2.将mongodb作为类似redis ,memcache来做缓存db,为mysql提供服务,或是后端日志收集分析。 考虑到mongodb属于nosql型数据库,sql语句与数据结构不如mysql那么亲和 ,也会有很多时候将mongodb做为辅助mysql而使用的类redis memcache 之类的缓存db来使用。 亦或是仅作日志收集分析。

Mysql 和Redis:

(1)类型上
从类型上来说,mysql是关系型数据库,redis是缓存数据库
(2)作用上
mysql用于持久化的存储数据到硬盘,功能强大,速度较慢,基于磁盘,读写速度没有Redis快,但是不受空间容量限制,性价比高
redis用于存储使用较为频繁的数据到缓存中,读取速度快,基于内存,读写速度快,也可做持久化,但是内存空间有限,当数据量超过内存空间时,需扩充内存,但内存价格贵
(3)需求上
mysql和redis因为需求的不同,一般都是配合使用。
需要高性能的地方使用Redis,不需要高性能的地方使用MySQL。存储数据在MySQL和Redis之间做同步。

Redis

为数据库挡刀,提高服务端接口的访问速度。作为第三方能够设置分布式锁,或者是作为RabitMQ避免重复消息提交的解决方式。

五种数据类型

  1. key-string: 普通的键值对,作为常规的key-value缓存应用
  2. key-hash: 就是map,一个string类型的field和value的映射表,适合存储对象
  3. key-list: 有序集合,简单的字符串列表,可以用作消息队列
  4. key-set: 无序、不可重复,可以用作去重
  5. key-zset: 有序(根据score)、不可重复,用于排序

redis保持mysql数据一致

  1. Mysql持久化数据,Redis只读数据

    1. Redis只读数据:不要求强一致性的读请求走redis,要求强一致性的直接从mysql读取。
    2. 数据首先写到数据库,之后更新redis(先写redis去过写入失败事务回滚会造成redis中存在脏数据)
  2. Mysq和Redis处理不同的数据类型
    1. Mysql处理实时性数据,比如金融数据,交易数据。
    2. Redis处理实时性要求不高的数据,例如网站热门排行榜、好友列表等在并发不高情况下,读操作优先读取redis,不存在再访问Mysql,并把读到的数据写会redis。写操作直接写mysql,成功后再写入reds(可以在Mysql端定义CRUD触发器)。
  3. 延迟双删方式
    1. 项目整合quartz等定时任务框架,实现延时3-5秒再去执行最后一步(最优)
    2. 创建线程池,线程体中延时3-5秒去执行最后一步任务(中等)
    3. 单独创建一个线程去实现延时执行(最差)

redis集群模式及特点

  1. **主从模式:**主机负责读写,从机只能进行读。可以提高读的并发能力,单点故障通过手动修改master解决。
  2. **哨兵模式:**在主从模式的基础上,可以解决单点故障,但是集群容量有限。
  3. 去中心化模式:
    1. 多台master提供读写,对应的slaver只提供读,当master故障,slaver立即升级为master,解决单点故障问题。
    2. 大幅度提高读写能力,提高redis集群容量。
    3. slot(hash槽):Reids集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16效验后对16384取余来决定放在哪个槽,集群的每个节点负责一部分hash槽

setnx、setex的作用

  1. setnx 使用场景 ==> 分布式锁(保证多个应用之间的线程安全)
  2. setnx key value 如果key存在,则返回0,什么也不做,如果key不存在,则返回1z,设置值。
  3. setex 为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。

redis持久化机制

  1. rdb:默认方式,将整个内存生成镜像文件,耗费性能且发生异常时数据丢失较多。生成慢,异常恢复快。
  2. aof:将所有增删改生成日志文件,在文件末尾追加,耗费性能少,但可能发生文件过大问题。异常恢复较慢,但是安全零丢失。

过期策略

  1. 定期删除:redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。
  2. 惰性删除:在获取某个 key 的时候会检查一下 ,这个 key 如果设置了过期时间且过期就会删除,不返回任何东西。
  3. 一般采用定期删除 + 惰性删除的方式。
  4. 如果定期删除漏掉了很多过期 key,同时也没有查进行惰性删除,可能导致大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,则需要内存淘汰机制

内存淘汰机制

  1. noeviction: 内存满时新写入操作报错。
  2. allkeys-lru:在键空间中,移除最近最少使用的 key(这个是最常用的)。
  3. allkeys-random:在键空间中,随机移除某个 key。
  4. volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的 key。
  5. volatile-random:在设置了过期时间的键空间中,随机移除某个 key。
  6. volatile-ttl:在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

缓存降级

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

缓存击穿

  1. 热点key处于集中式高并发访问情况下key失效,造成大量的请求击穿缓存,直接请求数据库。
  2. 将热点数据设置为永远不过期。
  3. 实现互斥锁,等待第一个请求构建完缓存之后再释放锁,进而其它请求才能通过该 key 访问数据。

缓存穿透

  1. 无效ID,直接访问db,造成db压力
  2. 布隆过滤器,通过一定算法整合所有id,可以快速判断当前id是否在集合内
  3. 把无效的ID缓存起来,并设置一个很短的超时时间
  4. 将所有id放入mysql set集合中,如果集合中没有,返回null
  5. 设置查询最大id

缓存雪崩

  1. 缓存同一时间批量失效或缓存机意外发生了全盘宕机,导致大量的访问直接访问db
  2. 采用固定失效时间+随机时间段,保证所有的缓存不会同一时间失效。
  3. 采用redis高并发,避免全盘崩溃
  4. redis持久化,一旦重启,快速恢复缓存数据

压测

  1. 使用工具JMeter
  2. tps:每秒交易量
  3. qps:每秒响应请求数
  4. RT:响应时长

JVM

创建对象的五种方式

  1. new Student();

  2. Student.class.newInstance(); 反射

  3. Constructor<student> a = Student.class.getConstructor();

    a.newInsance(); 反射

  4. student.clone(); jvm会创建一个新的对象,并全部拷贝,不调用构造函数,类需要先实现Cloneable接口并实现其定义的clone方法

  5. 反序列化 类需要实现Serializable

类加载过程

  1. 转化二进制流=>内存中生成Class对象
  2. 验证(数据类型、是否有父类、语义、类型转化是否正确等)
  3. 准备(静态变量分配在方法区内存及初始化默认值)
  4. 解析
  5. 初始化
  6. 使用

JVM加载Class文件的原理机制

JVM的类加载器将编译生成的.class文件按照要求和一定的规则加载到内存中,组成完整的java应用程序(把类文件读取到内存中)

加载分为隐式加载和显示加载,隐式加载是指程序在使用new等方式创建对象时会隐式的调用类的加载器把对应的类加载到JVM中,显示加载是通过直接调用class.forName()方法把所需的类加载到JVM中。类的加载时动态的,不会一次性将所有类全部加载。

装载(导入class文件)=>链接=>检查=>准备(给类中静态变量分配存储空间)=>解析=>初始化(对静态变量和静态代码块执行初始化工作)

内存泄漏的情况

  1. 长生命周期对象持有段生命周期对象的引用
  2. 修改存入HashSet集合中参与计算哈希值的字段

类加载器双亲委派模型机制

当一个类收到了类加载请求时,不会自己先去加载类,而是将其委派给父类加载.如果此时父类不能加载,反馈给子类,由子类去完成的加载

JDK

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waiEuWdu-1650358644426)(img\jdk结构.png)]

jvm内存划分

jvm本质是一个程序,也是Java可移植性的基础。项目仅需要编译一次生成.class文件,通过类加载器加载到内存区,之后进行解释执行。

  1. 类加载器(ClassLoader):加载class到JVM中
  2. **执行引擎:**负责执行class文件中包含的字节码指令
  3. **本地库接口:**连接本地方法(使用native修饰,不显示方法体,使用非Java语言实现的方法体方法)
  4. **内存区:**JVM运行的时候操作所分配的区域,运行时内存区主要可以划分为5个区域
    1. 方法区(Method Area):用于存储类结构信息的地方(常量池、静态变量、构造函数等)
    2. java堆(Heap):存储java对象和实例的地方。方法区和堆是被所有java线程共享的。堆区是GC的主要区域。
    3. java栈(Stack):每个线程创建的同时都会创建 jvm栈,存储局部变量表、操作栈、方法返回值等,线程私有。
    4. 本地方法栈(Native Method Stack):和java栈的作用差不多,是为JVM使用到的native方法服务的。
    5. 程序计数器(PC Register):jvm程序是通过线程轮流切换的多线程执行,用于存储每个线程下一步将执行的 jvm 指令,确保线程切换回来还能恢复到原先状态。如果使用的是native方法,则内部为空。

弱年代假设

1. 越早分配的对象越容易失效;
2. 老对象很少会引用新对象。

内存相关

Java 内存泄漏与内存溢出

内存泄漏:是指程序中间动态分配了内存,但在程序结束时没有释放这部分内存,从而造成那部分内存不可用的情况,重启计算机可以解决,但也有可能再次发生内存泄漏,内存泄漏是由软件设计缺陷引起的。

内存泄漏可以分为4类:

  1. 常发性内存泄漏。多次执行到发生泄露的代码
  2. 偶发性内存泄漏。特定环境下成为常发性
  3. 一次性内存泄漏。例如构造函数中发生泄漏
  4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

内存溢出:即用户在对其数据缓冲区操作时,超过了其缓冲区的边界;尤其是对缓冲区写操作时,缓冲区的溢出很可能导致程序的异常。

如何避免内存泄漏、溢出?

  1. 尽早释放无用对象的引用。
  2. 程序进行字符串处理时,尽量避免使用String,而应使用StringBuffer。
  3. 尽量少用静态变量。因为静态变量是全局的,GC不会回收。
  4. 避免集中创建对象尤其是大对象,如果可以的话尽量使用流操作。
  5. 尽量运用对象池技术以提高系统性能。生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
  6. 不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。

Mark-sweep 标记清除算法:

最基础的垃圾回收算法,分为标记(标记出所有需要被回收的对象)和清除(回收被标记的对象所占用的空间)两个阶段。问题是容易产生内存碎片,碎片太多可能会导致后续的过程中需要为大对象分配空间时无法找到足够的空间和提前触发新的一次垃圾收集动作。

Copying 复制算法

解决了Mark-sweep的缺陷.它将可用内存划分为大小相等的两个,每次只使用一块.当内存用完时,就将存活的对象复制到另一块上再将已使用的内存空间清理掉,这样一来就不容易出现内存碎片的问题。但是内存空间付出代价,能够使用的内存减少为原来的一半,但是他的效率和存活对象的数目有很大关系,如果存活对象很多,这种算法效率就大大降低,所以用于新生代。

Mark-compact 标记整理算法

解决Copying的缺陷,充分利用内存空间,标记和Mark-sweep一样,但是标记完成后不是直接清理可回收的对象,而是将存活的对象都向一端移动,然后清理掉边界之外的内存

GC

  1. 新生代:新对象产生的地方。年轻代被分为3个部分—Enden(伊甸园)区和两个Survivor(幸存)区(From和to)。当Eden区被对象填满时,就会执行Minor GC(次收集)。并把所有存活下来的对象转移到其中一个survivor区。Minor GC同样会检查存活下来的对象,并把它们转移到另一个survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间(一般15次)。
  2. 老年代:生命周期较长的对象。在老年代内存被占满时将会触发Full GC(全收集)System.gc() ,回收整个堆内存。
  3. 分代收集算法:把 Java 堆分为新生代和老年代,在新生代中因为每次垃圾收集时都有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成。老年代中因为对象存活率高,使用“标记-清理”或者“标记-整理”算法来进行回收。

**持久代:**用于存放JVM和Java类的元数据(描述数据的数据),持久代对垃圾回收没有显著的影响。

如何判断一个对象是垃圾:

  1. 引用计数法:每个对象都有一个引用计数器,当对象被引用一次,计数器就加1,当对象引用时效一次就减1,当计数器为 0,意味着对象是垃圾对象,可以被GC 回收。
  2. 可达性分析:通过一系列GC Roots的点作为起点,向下搜索。对于 GC Root 无法达到的对象便是垃圾对象, 随时可被 GC 回收。

java基础知识粗略整理相关推荐

  1. 2022全新Java基础知识整理,阿里大牛纯手绘思维导图文档

    最近趁着年初空闲的时间,总结一下自己这么多年来使用Java的一些心得体会,主要是一些Java基础知识点相关的, 分享给刚刚入门的Java程序员和需要巩固Java基础的朋友们,希望可以给大家一些经验,能 ...

  2. 【转】Java基础知识整理

    本博文内容参考相关博客以及<Java编程思想>整理而成,如有侵权,请联系博主. 转载请注明出处:http://www.cnblogs.com/BYRans/ PDF版下载链接:<Ja ...

  3. Java基础知识与集合部分面试题整理

    JAVA基础知识 一.JDK与JRE 1.JDK和JRE的区别 可从面向对象.主要作用和组成部分三方面对比.如下图所示: 2.JDK – Java Development Kit 1). 主要面向开发 ...

  4. Java 基础知识总结(下)-王者笔记《收藏版》

    上一篇 Java基础知识学习总结之(上) 下一篇 Java 集合容器篇面试题  (上) java毕业设计项目<100套>推荐 毕设/私活/大佬必备,一个挣钱的开源前后端分离脚手架 2W字梳 ...

  5. 【转】java基础知识总结

    Java基础知识总结 本博文内容参考相关博客以及<Java编程思想>整理而成,如有侵权,请联系博主. 转载请注明出处:http://www.cnblogs.com/BYRans/ PDF版 ...

  6. JAVA基础知识学习全覆盖

    文章目录 一.JAVA基础知识 1.一些基本概念 1.Stringbuffer 2.局部变量成员变量 3.反射机制 4.protect 5.pow(x,y) 6.final ,finally,fina ...

  7. java基础知识复习(上半)

    java基础知识复习 java为什么被发明? Green项目的确立,应用于像电视盒一样的消费类电子产品,语言本身本身中立. java的三大版本? javaSE的定位在于客户端,只要用于桌面应用软件的编 ...

  8. java水平测试_【考试】java基础知识测试,看你能得多少分?

    1 前言 共有5道java基础知识的单项选择题,每道20分,共计100分.解析和答案在最后. 2 试题 2.1 如下程序运行结果是什么? class Parent { public Parent(St ...

  9. java基础知识面试_Java 基础面试知识点

    Java 基础知识相关 Java中 == 和 equals 和 hashCode 的区别 对于关系操作符 == 若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的值是否相等 若操作数 ...

最新文章

  1. mysql backlog_一次优化引发的血案
  2. Java泛型详解-史上讲解最详细的,没有之一
  3. etcd分布式之消息发布与订阅
  4. 企业级项目实战讲解!java类内部定义枚举
  5. 中国牛逼的程序员有哪些?入职华为两天转正,半个月升主任
  6. .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)...
  7. MySQL数据库8(二十二)变量
  8. CS106A编程方法学二
  9. CDR插件开发之Addon插件004 - VS2022开发环境简介及个性化配置
  10. unity3d计算两点之间距离的方法
  11. 2022谷粒商城学习笔记(二十五)支付宝沙箱模拟支付
  12. Web Scraper爬取信息
  13. 华为防火墙简介及其工作原理
  14. 2015年11月小结
  15. 计算机桌面英文翻译,电脑显示器英语怎么说
  16. Java并发原理解析!docker命令
  17. daad转换器实验数据_DAAD留德日记作者:20分钟我拿到了德企总部的实习offer
  18. jquery转义html字符串,使用jQuery转义HTML字符串
  19. 乔姆斯基文法分类【0型1型2型3型文法】
  20. 国家质量基础设施(NQI)“一站式”公共服务平台建设方案

热门文章

  1. 计算机word论文,怎么用电脑Word写论文?
  2. Cve-2016-7434一把梭
  3. 分享120个ASP源码,总有一款适合您
  4. 贝尔数C语言,bzoj 3501 PA2008 Cliquers Strike Back——贝尔数
  5. 嵌入式Linux之我行——C+CGI+Ajax在S3C244
  6. RabbitMQ3:RabbitMQ的使用方法
  7. 35、ubuntu20.04搭建瑞芯微的npu仿真环境和测试rv1126的Debain系统下的yolov5+npu检测功能以及RKNN推理部署以及RTSP视频流解码
  8. 分享一个会遮掩的吊炸天登录页面
  9. 搜狗都上市了,王小川还是单身,难道要找AI当女朋友?
  10. Http 通过setHeader隐藏ip