1. HashMap
1 | public class HashMap<K,V> extends AbstractMap<K,V> |
未完待续….仍需要查阅大量资料学习….
2.Java 内存分配
- 栈区:方法区内的局部变量,对象的引用等 优点:速度快,缺点:大小和生命周期必须是确定的。
- 堆区:new出来的对象,垃圾回收器回收的是堆区的内存
- 方法区(静态区):静态变量,常量,保存类信息
3.垃圾
什么是垃圾?要通过科学的方法才能判断是否是为垃圾,有两种方法
- 引用计数法:可能会导致内部循环
- 可达性分析:是否可达
借助一个图快速理解 引用计数法:
代码:
1 | /**循环问题*/ |
再看下这个 可达性分析 :
ObjD
和 ObjE
虽然内部存在引用,但是定点不可达,所以也是垃圾回收的对象。
4.垃圾回收的方法
标记-清除:减少停顿时间,但会造成内存碎片
标记-整理:可以解决内存碎片,但是会增加停顿时间
复制清除:从一个地方拷贝到另一个地方,适合有大量回收的场景,比如:新生代回收
- 优点:效率高于标记清除,活着的对象是整齐排列的,没有内存碎片
- 缺点: 浪费空间,毕竟如果按照1:1比例划分空间的话,那么将会有50%的空间被浪费。不过在jvm中,新生代空间并不是按照1:1来划分的,而是按照8:1:1的比例分为一个
eden
区 和两个survivor(survivor0,survivor1)
区,然后一个eden
区,两个survivor
区。大部分对象在Eden
区中生成。回收时先将eden
区存活对象复制到一个survicor0
区,然后清空eden
区,当这个survivor0
区也存放满了时,则将eden
区和survivor0
区存活对象复制到另一个survivor1
区,然后清空eden
区和survivor0
区,此时survivor0
区是空的,然后将survivor0
区和survivor1
区交换,即保持survivor1
区为空,这样的过程也叫做Minor GC
,每进行Minor GC
一次,存活着的对象的年龄就会+1,当存活着的对象的年龄到达15岁时,就会被送进年老代。 当然,当整个survivor1
区不足以存放Eden
和survivor0
的存活对象时,也会将存活对象直接放到年老代。若是年老代也满了就会触发一次Full GC
,也就是新生代,老年代都进行回收。
分代收集:把内存区域分成不同代,根据年代不同采取不同的策略;
- 新生代:存放新创建的对象,采用复制回收方法
- 年老代:这些对象垃圾回收的频率较低,采取标记整理法
- 永久代:存放Java本身的一些数据,当类不再使用时,也会被回收
手稿:
5. Full GC 触发的条件
- 调用
System.gc
时,系统建议执行Full GC
但是不必然执行 - 年老代或者永久代空间不足
6. 线程调度
wait()
: object 方法,必须在同步代码块中使用,使当前线程处于等待状态,释放锁notify()
: 和wait
联合使用,通知一个线程,具体通知哪个由jvm 决定,使用不当可能发生死锁notifyAll()
: 和wait
方法联合使用,通知所有线程,具体哪个线程获得运行权jvm
决定sleep()
: 睡眠状态
7.线程同步
Synchronzied
修饰ThreadLocal
: 每个线程都有一个局部变量的副本,互不干扰。一种以空间换时间的方式。- 线程安全的容器和方法,可以实现线程同步,如:
Collections.SynchronizedList()
将List
转为线程同步;用ConurrentHashMap
实现hashmap
线程同步。
volatile
修饰的变量不会缓存在寄存器中,每次使用都会从主存器中读取;保证可见性,不保证原子性