Java 相关笔记

Posted on 2023年7月22日周六 技术

容器

HashMap

存一个 hash 桶,每个桶对应相应 hash 值的链表,从 JDK 1.8 开始,一个链表长度大于等于 8 的时候会将链表转化成红黑树。

ConcurrentHashMap

JDK 1.7 用的是分段锁,几个锁各自对应几个桶区间。到了 1.8 之后,改成了锁单个桶,如果对应桶为空,就 CAS,其他情况使用 synchronized 桶的头节点。

resize 操作 1.7 我不是很清楚,1.8 也是 CAS,通过 CAS 一个叫 sizeCtrl 的 field 实现只有一个线程在 resize。

https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E5%AE%B9%E5%99%A8.md 上面说 1.7 的 HashMap 和 ConcurrentHashMap 是头插,即把插入元素放到桶的第一个位置,但是自己看1.8的源码都是尾插了。原因是因为扩容的时候是 for 元素拆入,这个时候头插会改变列表的顺序(如果一个几个元素还在一个桶中,它们的顺序会倒过来),如果并发发生,可能会出现链表成环的问题。

WeekHashMap

WeekHashMap 的 Entry 继承自 WeakReference,被 WeakReference 关联的对象在下一次垃圾回收时会被回收,可用作缓存。

相关 WeakReference 都被一个 ReferenceQueue 串起来,一些特定的操作会触发清空操作,用来清除 queue 中已被清空的 Entry。

HashTable

方法命名和 HashMap 稍有不同,比如 elements (values()), contains (containsValue)。

HashTable 不支持 null value。

内部 hash 桶相关的数据结构,和 HashMap 相比也是类似的。

两者扩容的方法是不一样的,HashMap 的大小始终是2的幂次,HashTable 则是尽量使用奇数(素数),初始大小为 11,每次扩充为 2n + 1。

HashMap 的优点是计算可直接使用位运算,但是幂次会导致不均匀,所以 HashMap 的 hash 过程中还会加上一个打散操作。

HashTable 是线程安全的,因为使用了 synchronized。

运行时数据区域

垃圾回收

JVM使用可达性分析算法来判断一个对象是否可以被回收,以GC Roots为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。

GC Roots一般包含以下内容:

垃圾回收算法

垃圾收集器

并发

线程安全的几种方式

互斥同步,锁有公平和不公平之分,公平代表多个线程必须按照申请锁的时间顺序来获得锁,不公平则不需要。

下面是Java提供的两种锁机制:

封装类型

中断

和Kotlin的各种中断机制很类似。

Metaspace

替代了永久代(permanent generation),其作用是:

为什么OOM不会被垃圾回收阻止?貌似是一些实现的问题

永久代最大的缺点是其大小是不可以动态调节的。