作为学习JVM的一个小结,肯定还有很多错误,还请多多指正
java中的内存分为四种:
堆:线程共享,堆中存放是对象,所有的运行时对象存放在堆中;
桟:java中桟的单位是桟帧,线程私有的,每个被调用的方法都会创建一个桟帧用以存放方法的局部变量,操作数桟,动态链表,方法出口等等,可分为为java方法服务的java方法桟和为本地方法服务的本地方法桟;
程序计数器:线程私有,相当于字节码行号指示器,用于指示需要执行的下一条指令。
方法区:线程共享,存放加载后的类信息,常量,静态变量,以及即时编译器JIN编译后的代码。
常量池:存放类中的常量,基本类型的常量和引用类型的常量(final),字符串常量以及一些以文本形式出现的符号引用;
类加载机制:
类加载过程:
加载:通过一个类的全限定名找到对应的class文件,加载进内存,并创建对应的Class对象;
验证:检查 class文件是否符合当前jvm的要求;
准备:为类变量分配内存,并初始化为默认的零值;如果类变量被final修饰,则初始化为当前值;
解析:将常量池中的符号引用替换为直接引用。
初始化:运行代码中的赋值语句与静态块。
类加载器:
BootstrapClassLoader:启动类加载器,由c++实现,jvm自身的一部分,负责加载jre/lib/下面的标准类;
ExtClassLoader:扩展类加载器,由java 语言实现,负责加载 jre/ext/下面的标准类;
AppClassLoader:系统类加载器,由java 语言实现,负责加载classpath路径下的类;
双亲委派模式:
- 即如果加载器遇到一个加载某类的请求,会将此请求委托给它的父类进行加载,如果父类不能加载再自己加载。
- 好处: 避免重复加载,核心API类型不会随意更改。
GC
判断对象是否存活,有两种算法:
引用计数法:
每当一个对象被引用,就将此对象的计数器+1,引用失效计数器-1,如果引用计数器为零则对象可被回收。很难解决相互引用的问题。可达性分析发(根搜索算法):
通过一系列称作“GC ROOTS”的对象作为起始点,将所有的对象看作节点,如果一个对象持有另一个对象的引用,就代表一个对象到另一个对象可达,从GC ROOTS开始遍历,没有被遍历到的对象代表可被回收。- 可作为GC ROOTS的对象:
- 桟中方法变量的引用对象,
- 方法区中类变量的引用对象,
- 方法区中常量引用的对象,
- 本地方法桟中JNI引用对象。
GC算法
标记-清除算法:
- 标记存活对象,清除其余对象;容易产生内存碎片。
- 坏处:降低内存使用率。复制算法:
- 每次使用一般=半内存空间,回收时将存活对象复制到另一半内存空间。
- 好处:克服标记-清除算法产生内存碎片的问题;
- 坏处:并不适合存活对象较多的场合,如老年代,浪费内存空间。标记-压缩算法:
- 标记存活对象,回收时,将将存活对象压缩到一边,清除边界外的区域。
- 好处:克服了复制算法的空间浪费的问题。
- 坏处:适合存活对象较多的场景,如老年代。分代回收算法:
- 根据对象的生存周期分为新生代与老年代。根据不同的代选取不同的算法,新生代使用复制算法,因为对象朝生暮死,复制对象效率高,工作量也小;老年代使用标记-压缩或者标记-清除算法。
收集器:
serial:串行收集器:新生代使用复制算法,老年代使用标记压缩算法。
parNew:并行收集器:新生代使用多线程,复制算法,老年代依旧使用单线程标记压缩算法。
parallel:新生代使用复制算法,老年代使用标记压缩算法。同时让新生代和老年代都并行收集。可以控制停顿时间以及吞吐量。
cms:并发标记清除收集器,新生代使用复制算法,老年代使用标记清除算法,由于没有对对象的移动,清除阶段可以同用户线程共同运行。
- 初始标记:标记 可达对象;单独,短暂停顿。
- 并发标记:标记所有对象;与用户线程一起,没有停顿。
- 重新标记:标记在并发阶段用户线程新产生的对象。单独,短暂停顿。
- 并发清除:基于标记结果直接清除对象,与用户线程一起,没有停顿。
- 并发重置:为下一次GC做准备。- 特点:与用户线程同时运行,导致GC阶段,系统效率下降。
清理不彻底。
- 特点:与用户线程同时运行,导致GC阶段,系统效率下降。