【温故而知新】Jvm与gc深入理解
学习了很久,作出以下总结,来温故而知新
一、Jvm内存模型规范(jmm)
1.cpu、高速缓存、主内存
cpu读写磁盘速度较慢。
cup读写朱内存速度目前也有瓶颈。
cpu读写告诉缓存目前最快。
但是遇到一个问题,就是高速缓存的一致性问题。
各个高速缓存与朱内存都存在可见性、一致性问题。
2.内存屏障——volatile 关键字
作用:(1)阻止屏障两侧的指令重排序——c与汇编在高并发情况下,排序问题。
(2)强制保持一致性——将高速缓存数据写入主内存。
3.底层原理:
volatile底层使用汇编指令,mesi协议。即总线嗅探机制,cpu会立即情况高速缓存数据,去拿主内存数据。
4.JVM设计
用JMM规范来屏障各个硬件和system的访问差异,保证各个平台数据一致性。
(这里就注意,c是直接访问硬件的内存,不安全,会指令重排序)
但是Java就可以保证任意时间数据的可见性、有序性
二、JVM分区
1.程序计数器:
JMM规范中,唯一没有OOM的区域。程序行号计数器,线程私有,记录程序读取行号的位置。
2.栈:
Java方法执行的内存模型。
栈帧构成:(1)局部变量
(2)操作
(3)动态链表
1.栈的功能主要是进栈和出栈
2.栈实现先进后出
(4)方法出口(返回地址)
OOM异常根本原因:
(1)线程请求栈深度超过了栈深度
(2)虚拟机空间动态扩展,扩展到了无法申请。
3.本地方法栈
存放native方法的地方,c与c++(也叫字节码服务)
4.堆
存放大多数对象的实例
为什么说是大多数呢?
因为有两个概念:JIT即时编译器、逃逸设计
即时编译器:可以把把Java的字节码,包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序)
逃逸分析:通过逃逸分析来决定某些实例或者变量是否要在堆中进行分配,如果开启了逃逸分析,即可将这些变量直接在栈上进行分配,而非堆上进行分配。这些变量的指针可以被全局所引用,或者其其它线程所引用。
1.JIT即时编译,可以把java的字节码直接执行。。。不需要在堆中创建实例
2.逃逸设计也是如此,直接将实例在栈上分配,且全局引用,也不再堆中。
堆得特点是:
1.所有线程共享
2.大多数的对象实例
3.GC的主要区域——收集器(分代算法)
三、GC垃圾回收机制
要搞清楚GC,就要说一说对象,因为GC其实就是在回收对象。
1.对象内存分布
(1)对象头
分为32位和64位;对象头放着Hash码、年代、锁标识
(2)实例数据
(3)对齐填充
(4)对象访问定位
分为句柄和指针
2.OOM
(1)请求栈深度大于栈实际深度
(2)动态扩展的内存无法申请足够的资源
重要指标 --verbose
其实对于Java开发来说,gc无需关注,但是c、c++开发需要使用 delete、free来释放
3.哪些内存需要回收?哪些不需要回收?
(1)不回收:程序计数器、栈、本地方法栈——线程死即死。
(2)回收:堆、方法区——动态的分配与回收
4.分代算法
新生代
老年代
永久代,也叫持久代(jdk1.8以后,叫元空间)
为什么要分代?
因为效率问题
5.收集算法:
标记/清除(基础)
复制 (eden空间、From Survivor、To Survivor【保留空间】)
标记/整理
6.枚举根节点算法
7.垃圾收集器:
年轻带:serial、parNew
老年代:serial old、paralled old 、 cms
特殊:G1收集器
8.Minor GC、Major GC、FULL GC、mixed gc
full gc是对新生代、老年代、永久代统一的回收。会停止程序,优化的点也在减少full gc的次数。
full gc的另一个叫法是:stop the world