@@ -15,12 +15,13 @@ pin: true
1515
1616装箱:` Integer i = Integer.valueOf(10) ` , 拆箱:` int n = i.intValue() `
1717
18+ 反射:指动态获取的信息以及动态调用对象的方法的功能。在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
1819
1920# 内存结构
2021
2122![ ] ( https://img-blog.csdnimg.cn/cb59671dff1f4588b5d845f60874013b.png )
2223
23- 运行时数据区域包含线程私有的程序计数器、虚拟机栈、本地方法栈,线程共享的堆(包含字符串常量池)和非运行时数据区的元空间( 包含类常量池和运行时常量池)、直接内存 。
24+ 运行时数据区域包含线程私有的程序计数器、虚拟机栈、本地方法栈,线程共享的堆(包含字符串常量池)。直接内存(堆外内存)包含元空间( 包含类常量池和运行时常量池)。
2425- 程序计数器:与操作系统中的程序计数器类似,为了线程切换后能恢复到正确的执行位置,是唯一一个不会出现 ` OutOfMemoryError ` 的内存区域。
2526- 虚拟机栈:以帧为单位,帧由局部变量表、操作数栈、动态链接、方法返回地址组成。每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
2627 - 局部变量表:主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、** 对象引用** (reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)
@@ -33,12 +34,27 @@ pin: true
3334- 类常量池:每个java文件被编译成class文件后会有一项常量池,用于存放编译器生成的** 字面量** 和** 符号引用** 。在** 编译** 阶段,存放的是常量的** 符号引用** 。
3435- 运行时常量池:是在** 类加载完成后** ,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析阶段,将符号引用替换成直接引用,与字符串常量池中的引用值保持一致。
3536
37+
38+ ## 方法区
39+
40+ ### 永久代和元空间的区别
41+
42+ 在 hotpot 虚拟机中,JDK7以前的方法区是用永久代实现的;在JDK8后,方法区用元空间实现的。最大的区别:元空间位于本地内存上;而永久代位于虚拟机内存中,与堆是连续的一块内存。
43+
44+ ### 垃圾回收
45+ 只有 Full GC 时执行垃圾回收,主要回收两部分内容:
46+ 1 . 常量池中废弃的常量
47+ 2 . 不再使用的类型,类需要同时满足下面 3 个条件才能被卸载:
48+ 1 . 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
49+ 2 . 加载该类的 ClassLoader 已经被回收。
50+ 3 . 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
51+
3652[ class常量池、字符串常量池和运行时常量池的区别] ( https://blog.csdn.net/xiaojin21cen/article/details/105300521 )
3753
3854
3955# [ 内存模型(JMM)] ( https://zhuanlan.zhihu.com/p/258393139 )
4056
41- JMM 旨在提供一个统一的可参考的规范,屏蔽平台内存访问差异性。这个规范为读写共享变量时如何与内存交互提供了规则和保证 。并发编程中,程序会因为 CPU 多级缓存或指令重排序等出现问题,因此需要一些规范要保证并发编程的可靠性。
57+ JMM 旨在提供一个统一的可参考的规范,屏蔽平台内存访问差异性。这个规范为多线程读写共享变量时如何与内存交互提供了规则和保证 。并发编程中,程序会因为 CPU 多级缓存或指令重排序等出现问题,因此需要一些规范要保证并发编程的可靠性。
4258
4359关键概念包括:
4460- 主内存:表示所有线程都可以访问的共享内存。线程不能直接读写主内存中的变量。
@@ -124,12 +140,6 @@ Tomcat中的类加载器:
124140Java的SPI:SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
125141
126142
127-
128- 1.6 -> 1.7 -> 1.8:
129- - 1.6 -> 1.7: 字符串常量池从方法区(永久代)中移到堆中。原因: GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收。
130- - 1.7 -> 1.8: 将运行时数据区方法区(永久代)移动到直接内存中,字符串常量池仍然在堆中。
131-
132-
133143## 对象的创建过程
1341441 . 类加载检查
1351452 . 分配内存:指针碰撞或空闲列表
@@ -195,19 +205,19 @@ JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻
195205## [ 垃圾收集器] ( https://cloud.tencent.com/developer/article/1592943 )
1962061 . Serial 收集器,单线程、复制算法的新生代收集器
1972072 . ParNew 收集器,多线程、复制算法的新生代收集器,老年代采用Serial Old收集器
198- 3 . Parallel Scavenge 收集器,多线程、复制算法的新生代收集器 ,高吞吐量。
208+ 3 . Parallel Scavenge 收集器,多线程、** 复制算法 ** 的新生代收集器 ,高吞吐量。
1992094 . Serial Old 收集器,单线程、标记-整理算法的老年代收集器。
200- 5 . Parallel Old 收集器,多线程、标记-整理算法的老年代收集器 。
210+ 5 . Parallel Old 收集器,多线程、** 标记-整理算法 ** 的老年代收集器 。
2012116 . CMS(Concurrent Mark Sweep) 收集器,标记-清除算法,以获取最短回收停顿时间为目标的收集器。JDK14正式移除。
2022127 . G1(Garbage-First) 收集器,标记-整理 + 复制算法,内存碎片的产生率大大降低。JDK9-JDK17的默认垃圾收集器。
203213
204214![ ] ( https://raw.githubusercontent.com/CompetitiveLin/ImageHostingService/picgo/imgs/202305091408302.png )
205215_ G1收集器内存模型_
206216
207217垃圾收集器发展历程:
208- - JDK8 默认 Parallel Scavenge + Parallel Old
218+ - JDK8 默认 Parallel Scavenge(标记-复制) + Parallel Old(标记整理)
209219- JDK9 默认 G1
210- - JDK11 提出ZGC
220+ - JDK11 提出 ZGC
211221- JDK14 CMS 被移除
212222
213223
@@ -379,7 +389,7 @@ Java 中的线程分为两种:
379389- Collections.synchronizedList() 读少写多的情况
380390
381391[ HashMap 知识点] ( https://zhuanlan.zhihu.com/p/151027796 )
382- - HashMap: 乱序,数组+链表+红黑树,链表长度大于8转红黑树 ,红黑树节点个数小于6转链表。
392+ - HashMap(JDK8) : 乱序,数组+链表+红黑树+尾插法,链表长度大于8并且数组长度大于64时转红黑树 ,红黑树节点个数小于6转链表。JDK7时用数组+链表+头插法(可能造成循环链表)实现 。
383393- LinkedHashMap: 按插入顺序排序
384394- TreeMap: 按字典序排序,因为是按字典序排序的,所以键肯定不能为null,值可以为null
385395- IdentityHashMap:利用哈希表实现Map接口,不同的是,其比较键(或值)时,使用引用相等性代替对象相等性。
@@ -389,13 +399,16 @@ Java 中的线程分为两种:
389399
390400为什么计算哈希值采用低十六位和高十六位异或操作:
391401
392- 计算数组下标是与操作,只有低 n 位进行与操作,高位不参与任何操作 -> 为了增大散列程度减小哈希碰撞,因此将高十六位参与进哈希值的计算。
402+ 计算数组下标是与操作,只有低 n 位进行与操作,高位不参与任何操作 -> 为了增大散列程度减小哈希碰撞,因此将高十六位参与进哈希值的计算。
393403
394404put() 的流程:
3954051 . hashcode的高十六位和低十六位进行异或运算
3964062 . (n - 1) & hash 计算数组下标,当 n 为二次幂时,等价于取余操作((n - 1)& hash = hash % n)。
3974073 . 判断当前下标是否有元素,若有元素,使用尾插法。再根据链表长度判断是否需要转换成红黑树。
398408
409+ 第一次扩容:执行第一次 put() 操作时,如果数组为空便会执行第一次扩容操作,初始化数组容量为默认大小。
410+
411+
399412扩容的过程:
4004131 . 将数组扩容成原数组的两倍
4014142 . 如果没有哈希冲突的节点,使用 ` e.hash&(newCap - 1) ` 计算新的桶位置
@@ -423,7 +436,7 @@ NULL key AND NULL value:
423436
424437ConcurrentHashMap JDK7 vs JDK8
425438- JDK7: 数组 + 链表。先定位 Segment,再定位桶。底层结构是继承了ReentrantLock的Segment数组。可以看成是由线程安全的HashMap组成的一个map数组,数组的长度决定了支持的最大的并发量。
426- - JDK8: 数组 + 链表 + 红黑树。可以直接定位到桶。链表中的元素超过8后 ,将链表结构转换成红黑树。通过对Node数组以CAS方式实现扩容和对Node数组的每个元素的synchronized保证ConcurrentHashMap整体的线程安全。
439+ - JDK8: 数组 + 链表 + 红黑树。可以直接定位到桶。链表中的元素超过8并且数组长度大于64后 ,将链表结构转换成红黑树。通过对Node数组以CAS方式实现扩容和对Node数组的每个元素的synchronized保证ConcurrentHashMap整体的线程安全。
427440
428441![ ] ( https://raw.githubusercontent.com/CompetitiveLin/ImageHostingService/picgo/imgs/202305151605481.png )
429442
0 commit comments